postcodes
================
**A dutch postcode lookup app for Django.**
By default we use the free (but rate limited) API from [postcode.go-dev.nl](https://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](#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:
```bash
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`:
```python
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.
```python
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
```html
{% 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({
// Add invalid classes to the input fields when they are invalid
validate: true,
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:
```python
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.
Raw data
{
"_id": null,
"home_page": "https://github.com/Nigel2392/postcodes",
"name": "django-postcodes",
"maintainer": null,
"docs_url": null,
"requires_python": ">=3.8",
"maintainer_email": null,
"keywords": null,
"author": "Nigel",
"author_email": "nigel@goodadvice.it",
"download_url": "https://files.pythonhosted.org/packages/26/a6/ea1c4113470c9c8e67b8ff65607c342ebb1bea43ed4d8488c4a029a8edaf/django_postcodes-1.0.6.tar.gz",
"platform": null,
"description": "postcodes\r\n================\r\n\r\n**A dutch postcode lookup app for Django.**\r\n\r\nBy default we use the free (but rate limited) API from [postcode.go-dev.nl](https://postcode.go-dev.nl/).\r\n\r\nYou must create an account and get an API key to use this package (in the default configuration).\r\n\r\nThis API key must be set in the [settings](#settings).\r\n\r\n**Features**\r\n\r\n* Lookup addresses by postcode and home number\r\n* Customizable form fields\r\n* Customizable API endpoint\r\n* Autofill form fields with the address data\r\n* Validation of form fields when the address is not found\r\n* Validation of form fields when user input was given (regex, min, max.)\r\n\r\nQuick start\r\n-----------\r\n\r\n1. Install the package via pip:\r\n\r\n ```bash\r\n pip install django-postcodes\r\n ```\r\n\r\n2. Add 'postcodes' to your INSTALLED_APPS setting like this:\r\n\r\n ```\r\n INSTALLED_APPS = [\r\n ...,\r\n 'postcodes',\r\n ]\r\n ```\r\n3. Add the postcodes URL to your project's `urls.py`:\r\n\r\n ```python\r\n path('postcodes/', include('postcodes.urls', namespace='postcodes')),\r\n ```\r\n\r\n\r\n## Example usage\r\n\r\nFirst we must define the appropriate form.\r\n\r\nThis is an example of a form that uses the default API endpoint (custom ones can be defined in the settings):\r\n\r\nEvery attribute can be customized, except for the postcode and home number fields, which are required for the lookup.\r\n\r\nThese are hard-coded.\r\n\r\nAll other elements provided in the `bind` object will be filled in with the data from the API.\r\n\r\n```python\r\nclass AddressForm(forms.Form):\r\n # These are required for the lookup\r\n postcode = forms.CharField(max_length=10, widget=forms.TextInput(attrs={\"placeholder\": \"1234 AB\", \"class\": \"postcode\", \"pattern\": \"^[0-9]{4}(\\s+|)[A-Z]{2}$\"}))\r\n home_number = forms.CharField(max_length=10, widget=forms.TextInput(attrs={\"placeholder\": \"123\", \"class\": \"home_number\"}))\r\n \r\n # Custom fields\r\n street = forms.CharField(max_length=255, widget=forms.TextInput(attrs={\"placeholder\": \"Main street\", \"class\": \"street\"}))\r\n city = forms.CharField(max_length=255, widget=forms.TextInput(attrs={\"placeholder\": \"Amsterdam\", \"class\": \"city\"}))\r\n municipality = forms.CharField(max_length=255, widget=forms.TextInput(attrs={\"placeholder\": \"Amsterdam\", \"class\": \"municipality\"}))\r\n province = forms.CharField(max_length=255, widget=forms.TextInput(attrs={\"placeholder\": \"Noord-Holland\", \"class\": \"province\"}))\r\n build_year = forms.IntegerField(widget=forms.NumberInput(attrs={\"placeholder\": \"1990\", \"class\": \"build_year\", \"pattern\": \"^[0-9]{4}$\", \"min\": \"1900\", \"max\": \"2022\"}))\r\n floor_area = forms.DecimalField(widget=forms.NumberInput(attrs={\"placeholder\": \"100\", \"class\": \"floor_area\", \"pattern\": \"^[0-9]{1,3}$\"})) # \"pattern\": \"^[0-9]{1,3}$\"\r\n geo_x = forms.DecimalField(widget=forms.NumberInput(attrs={\"placeholder\": \"52.123456\", \"class\": \"geo_x\"}))\r\n geo_y = forms.DecimalField(widget=forms.NumberInput(attrs={\"placeholder\": \"4.123456\", \"class\": \"geo_y\"}))\r\n rd_x = forms.DecimalField(widget=forms.NumberInput(attrs={\"placeholder\": \"123456\", \"class\": \"rd_x\"})) # Rijksdriehoek\r\n rd_y = forms.DecimalField(widget=forms.NumberInput(attrs={\"placeholder\": \"123456\", \"class\": \"rd_y\"})) # Rijksdriehoek\r\n```\r\n\r\nThen we can define our template\r\n\r\n```html\r\n{% extends 'base.html' %}\r\n\r\n{% block content %}\r\n <link rel=\"stylesheet\" href=\"{% static 'postcodes/css/postcodes.css' %}\">\r\n <script src=\"{% static 'postcodes/js/postcodes.js' %}\" data-api-url=\"{% url \"postcodes:api\" %}\"></script>\r\n\r\n <form method=\"post\">\r\n {% csrf_token %}\r\n {{ form.as_p }}\r\n <button type=\"submit\">Submit</button>\r\n </form>\r\n\r\n ...\r\n\r\n <script>\r\n document.addEventListener('DOMContentLoaded', function() {\r\n lookupPostcode({\r\n // Add invalid classes to the input fields when they are invalid\r\n validate: true,\r\n bind: {\r\n // These are required for the lookup\r\n postcode: document.querySelector('#id_postcode'),\r\n home_number: document.querySelector('#id_home_number'),\r\n\r\n // Custom fields returned by the API\r\n straat: document.querySelector(\"#id_street\"),\r\n woonplaats: document.querySelector(\"#id_city\"),\r\n gemeente: document.querySelector(\"#id_municipality\"),\r\n provincie: document.querySelector(\"#id_province\"),\r\n bouwjaar: document.querySelector(\"#id_build_year\"),\r\n vloeroppervlakte: document.querySelector(\"#id_floor_area\"),\r\n\r\n // Or optionally as a queryselector\r\n // If everything is a string - it is safe to omit the DOMContentLoaded eventListener\r\n latitude: \"#id_geo_x\",\r\n longitude: \"#id_geo_y\",\r\n rd_x: \"#id_rd_x\",\r\n rd_y: \"#id_rd_y\",\r\n },\r\n success: function(addr) {\r\n console.log(addr);\r\n },\r\n error: function(error) {\r\n console.log(error);\r\n }\r\n })\r\n });\r\n </script>\r\n\r\n{% endblock %}\r\n```\r\n\r\nThe form will now automatically fill in the address fields when a valid postcode and home number is entered.\r\n\r\nIf it is invalid or not found, the error callback will be called.\r\n\r\n## Settings\r\n\r\n### `ADDR_VALIDATOR_API_KEY`\r\n\r\nThe API key to use for the postcode lookup.\r\n\r\nThis will be used by the `postcodes.postcode.address_check` function.\r\n\r\n\r\n### `ADDR_VALIDATOR_API_URL`\r\n\r\nThe API URL to use for the postcode lookup.\r\n\r\nThis will be used by the `postcodes.postcode.address_check` function.\r\n\r\nIt should contain the `{postcode}` and `{home_number}` placeholders.\r\n\r\n\r\n### `ADDR_VALIDATOR_PARAMETER_FORMAT`\r\n\r\nAn actual function that formats the parameters for the API URL.\r\n\r\nThis may not be a path to a function, but the function itself.\r\n\r\nExample:\r\n\r\n```python\r\ndef default_parameter_formatter(**kwargs):\r\n return \"&\".join([f\"{key}={value}\" for key, value in kwargs.items()])\r\n```\r\n\r\n\r\n### `ADDR_VALIDATOR_ERROR_ATTRIBUTE`\r\n\r\nThe attribute in the response that contains the error message.\r\n\r\nThis will be used by the `postcodes.postcode.address_check` function.\r\n\r\nAddressValidationError exceptions will return the appropriate error message if the attribute is found.\r\n\r\n\r\n### `ADDR_VALIDATOR_CACHE_TIMEOUT`\r\n\r\nThe timeout for the cache in seconds.\r\n\r\nThis will be used by the `postcodes.postcode.address_check` function.\r\n\r\nIt is highly recommended to set this to a high number; by default it caches for a week.\r\n\r\nThe default endpoint is free to use, but has a rate limit.\r\n\r\n\r\n### `ADDR_VALIDATOR_API_KEY_ATTRIBUTE`\r\n\r\nThe attribute in the response that contains the API key.\r\n\r\nThis will be used by the `postcodes.postcode.address_check` function.\r\n\r\nIt is the header that should be sent with the request.\r\n\r\nDefaults to `X-API-Token`.\r\n\r\n\r\n### `ADDR_VALIDATOR_REQUIRES_AUTH`\r\n\r\nWhether the API requires authentication.\r\n\r\nThis will be used by the internal view to check if the user is authenticated.\r\n\r\nIf the user is authenticated; the view will return the address data.\r\n\r\nThis does not matter if you use the `postcodes.postcode.address_check` function.\r\n",
"bugtrack_url": null,
"license": "GPL-3.0-only",
"summary": "A Django app to manage postcode/home number address forms",
"version": "1.0.6",
"project_urls": {
"Homepage": "https://github.com/Nigel2392/postcodes"
},
"split_keywords": [],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "26a6ea1c4113470c9c8e67b8ff65607c342ebb1bea43ed4d8488c4a029a8edaf",
"md5": "c435648a25b97530ae84ecad6ce7c138",
"sha256": "58e13fcd154716af918966099ba25b8a509a54c1c9c2726eedffe101a99e9177"
},
"downloads": -1,
"filename": "django_postcodes-1.0.6.tar.gz",
"has_sig": false,
"md5_digest": "c435648a25b97530ae84ecad6ce7c138",
"packagetype": "sdist",
"python_version": "source",
"requires_python": ">=3.8",
"size": 21825,
"upload_time": "2024-12-05T10:38:57",
"upload_time_iso_8601": "2024-12-05T10:38:57.526872Z",
"url": "https://files.pythonhosted.org/packages/26/a6/ea1c4113470c9c8e67b8ff65607c342ebb1bea43ed4d8488c4a029a8edaf/django_postcodes-1.0.6.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-12-05 10:38:57",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "Nigel2392",
"github_project": "postcodes",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "django-postcodes"
}