Skip to content

Commit

Permalink
Merge branch 'release/0.2.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
banagale committed May 12, 2020
2 parents edab738 + f71cddd commit c5df849
Show file tree
Hide file tree
Showing 8 changed files with 142 additions and 73 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ local_settings.py
db.sqlite3
media
dist/
*.egg-info/
*.egg-info/
.idea
63 changes: 38 additions & 25 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,30 +1,40 @@
# django-address
# Django Address

## Installation
**Django models for storing and retrieving postal addresses.**

Previously a patch for Django was required to make this app work, but as
of 1.7 the patch is no longer needed. Installation is now done as per
usual. The package is installed with:
---

# Overview
Django Address is a set of models and methods for working with postal addresses.

# Requirements
* Python (3.5, 3.6, 3.7, 3.8)
* Django (2.2, 3.0)

We **recommend** and only officially support the latest patch release of each Python and Django series.

# Installation
For more detailed instructions, [view the Readme for the example site](https://github.com/furious-luke/django-address/blob/master/example_site/README.md) included with this package.

```bash
pip install django-address
```

Then, add `address` to your `INSTALLED_APPS` list in `settings.py`:

```python
INSTALLED_APPS = (
...
'address',
)
```
INSTALLED_APPS = [
...
'django_filters',
]
```

You wil need to add your Google Maps API key to `settings.py` too:
```
GOOGLE_API_KEY = 'AIzaSyD--your-google-maps-key-SjQBE'
```

## The Model
# The Model

The rationale behind the model structure is centered on trying to make
it easy to enter addresses that may be poorly defined. The model field included
Expand Down Expand Up @@ -61,13 +71,13 @@ There are four Django models used:
locality -> Locality
```

## Address Field
# Address Field

To simplify storage and access of addresses, a subclass of `ForeignKey` named
`AddressField` has been created. It provides an easy method for setting new
addresses.

### Creation
## Creation

It can be created using the same optional arguments as a ForeignKey field.
For example:
Expand All @@ -80,7 +90,7 @@ For example:
address2 = AddressField(related_name='+', blank=True, null=True)
```

### Setting Values
## Setting Values

Values can be set either by assigning an Address object:

Expand All @@ -93,7 +103,7 @@ Values can be set either by assigning an Address object:
Or by supplying a dictionary of address components:

```python
obj.address = {'street_number': '1', route='Somewhere Ave', ...}
obj.address = {'street_number': '1', 'route': 'Somewhere Ave', ...}
```

The structure of the address components is as follows:
Expand All @@ -119,7 +129,7 @@ be set directly:
obj.address = 'Out the back of 1 Somewhere Ave, Northcote, Australia'
```

### Getting Values
## Getting Values

When accessed, the address field simply returns an Address object. This way
all components may be accessed naturally through the object. For example::
Expand All @@ -145,7 +155,7 @@ The model:
from address.models import AddressField

class Person(models.Model):
address = AddressField()
address = AddressField(on_delete=models.CASCADE)
```

The form:
Expand All @@ -168,13 +178,16 @@ The template:
</body>
```

## Disclaimer
## Project Status Notes

This library was created by [Luke Hodkinson](@furious-luke) originally focused on Australian addresses.

In 2015 Luke began working to abstract the project so it could handle a wider variety of international addresses.

This became the current `dev` branch. While good progress was made on this, the branch became stale and releases
continued under the current model architecture on master.

These instructions are a little shabby, I haven't had a whole lot of time to
devote to explaining things thoroughly. If you're interested in using this
but are having trouble getting it setup please feel free to email me at
furious.luke@gmail.com, I'll assist as best I can and update the instructions
in the process. Cheers!
The project is currently in triage, for releases 0.2.2 and 0.2.3, with a both a model re-architecture and updated
requirements for 0.3.0. Read more about the project path forward [in this issue](#98).

Also, *there will be bugs*, please let me know of any issues and I'll do my
best to fix them.
If you have questions, bug reports or suggestions please create a New Issue for the project.
9 changes: 2 additions & 7 deletions address/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models.fields.related import ForeignObject
from django.utils.encoding import python_2_unicode_compatible

try:
from django.db.models.fields.related_descriptors import ForwardManyToOneDescriptor
Expand Down Expand Up @@ -133,8 +132,7 @@ def to_python(value):
if isinstance(value, Address):
return value

# If we have an integer, assume it is a model primary key. This is mostly for
# Django being a cunt.
# If we have an integer, assume it is a model primary key.
elif isinstance(value, (int, long)):
return value

Expand All @@ -161,7 +159,6 @@ def to_python(value):
##


@python_2_unicode_compatible
class Country(models.Model):
name = models.CharField(max_length=40, unique=True, blank=True)
code = models.CharField(max_length=2, blank=True) # not unique as there are duplicates (IT)
Expand All @@ -178,7 +175,6 @@ def __str__(self):
##


@python_2_unicode_compatible
class State(models.Model):
name = models.CharField(max_length=165, blank=True)
code = models.CharField(max_length=3, blank=True)
Expand All @@ -204,7 +200,6 @@ def to_str(self):
##


@python_2_unicode_compatible
class Locality(models.Model):
name = models.CharField(max_length=165, blank=True)
postal_code = models.CharField(max_length=10, blank=True)
Expand Down Expand Up @@ -234,7 +229,6 @@ def __str__(self):
##


@python_2_unicode_compatible
class Address(models.Model):
street_number = models.CharField(max_length=20, blank=True)
route = models.CharField(max_length=100, blank=True)
Expand Down Expand Up @@ -307,6 +301,7 @@ class AddressField(models.ForeignKey):

def __init__(self, *args, **kwargs):
kwargs['to'] = 'address.Address'
kwargs['on_delete'] = models.CASCADE
super(AddressField, self).__init__(*args, **kwargs)

def contribute_to_class(self, cls, name, virtual_only=False):
Expand Down
2 changes: 1 addition & 1 deletion address/widgets.py
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ def render(self, name, value, attrs=None, **kwargs):
elems = [
super(AddressWidget, self).render(
name,
escape(ad.get('formatted', None)),
escape(ad.get('formatted', '')),
attrs,
**kwargs
)
Expand Down
62 changes: 46 additions & 16 deletions example_site/README.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,55 @@
Quickstart
==========
# Overview
This is a django demonstration project that shows how `django-address` can be used to geocode manually entered postal addresses.

Get a [Google Maps API Key] and update `GOOGLE_API_KEY` in [settings.py].
**The Landing Page**

```bash
# Set up virtual environment
virtualenv env
source env/bin/activate
<img alt="Screenshot of landing page" src="https://user-images.githubusercontent.com/1409710/81486802-50bc4500-920c-11ea-901e-2579e7ce93b2.png" width="450">

# Install requirements
pip install -r requirements.txt
**The Admin View**

# Migrate
python manage.py migrate
<img alt="Screenshot of django admin" src="https://user-images.githubusercontent.com/1409710/81486803-52860880-920c-11ea-8938-b5e216d29c40.png" width="450">

# Create a super admin to see results in Django Admin
python manage.py createsuperuser
# Setup
## Create virtual environment
* `virtualenv env`
* `source env/bin/activate`

## Install python requirements
* `pip install -r requirements.txt`

## Add Google Maps requirements
Create a Google Cloud Platform project and API Key
* Instructions for setting up an API key here: [Google Maps API Key]
* This requires the set up of a billing with Google
* The key will belong to a "project" in Google Cloud Platform

### Enable (activate) required Google Maps services for the project your key belongs to
This is hidden under Google Cloud Platform's console menu, under
**Other Google Solutions** > **Google Maps** > **APIs**. ([screenshot](https://user-images.githubusercontent.com/1409710/81484071-9d495580-91f7-11ea-891e-850fd5a225de.png))
* Google Maps _Javascript API_
* Google Maps _Places API_

### Update this example_site django project's [settings.py].
* Add your key to `GOOGLE_API_KEY`

## Migrate
* `python manage.py migrate`

## Create a super admin to see results in Django Admin
* `python manage.py createsuperuser`

## Run server
* `python manage.py runserver`

# The Project
The page shows a simple form entry field
### Troubleshooting Google Maps
Check the browser console on the page for javascript errors. ([Screenshot of an error](https://user-images.githubusercontent.com/1409710/81484063-90c4fd00-91f7-11ea-8833-80a346c77f89.png))
* `ApiTargetBlockedMapError`: Your API key [needs authorization](https://developers.google.com/maps/documentation/javascript/error-messages#api-target-blocked-map-error) to use the above services.
* `ApiNotActivatedMapError`: Your API key [needs Google Maps services](https://developers.google.com/maps/documentation/javascript/error-messages#api-target-blocked-map-error) to use the above services.

***NOTE:** There is up to a several minute delay in making changes to project and api key settings. New keys can also take several minutes to be recognized.

# Run server
python manage.py runserver
```

[Google Maps API Key]: https://developers.google.com/maps/documentation/javascript/get-api-key
[settings.py]: example_site/settings.py
38 changes: 28 additions & 10 deletions example_site/person/templates/example/home.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,39 @@
<title>django-address</title>

<style>
.address {
width: 300px;
}
.address {
width: 300px;
}

.fixed-width {
font-family: monospace;
}

.bold {
font-weight: bold;
}
</style>

<!-- needed for JS/GoogleMaps lookup -->
{{ form.media }}
</head>
<body>
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>

<p>form.initial.address: {{ form.initial.address }}</p>
{% if not google_api_key_set %}
<p>This django-address example site is running correctly.
<p>However, you <span class="bold">must</span> set <span class="fixed-width">GOOGLE_API_KEY</span> in settings.py to use this demo.</p>
<p>See this <a target="_blank" href="https://github.com/furious-luke/django-address/blob/master/example_site/README.md">sample project's readme</a> for instructions.</p>
{% else %}
<h1>django-address demo application</h1>
<form action="" method="post">
{% csrf_token %}
{{ form }}
<input type="submit" value="Submit">
</form>
{% if success %}
<p>Successfully submitted an address.</p>
<p>View address model data in <a href="{% url 'admin:index' %}">django admin</a>.</p>
{% endif %}
<p>Total addresses saved: {{ addresses.count }}</p>
{% endif %}
</body>
</html>
21 changes: 17 additions & 4 deletions example_site/person/views.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,28 @@
from django.conf import settings
from django.shortcuts import render
from address.models import Address

from address.models import Address
from .forms import PersonForm


def home(request):
success = False
addresses = Address.objects.all()
if settings.GOOGLE_API_KEY:
google_api_key_set = True
else:
google_api_key_set = False

if request.method == 'POST':
form = PersonForm(request.POST)
if form.is_valid():
pass
success = True
else:
form = PersonForm(initial={'address': Address.objects.first()})
form = PersonForm(initial={'address': Address.objects.last()})

context = {'form': form,
'google_api_key_set': google_api_key_set,
'success': success,
'addresses': addresses}

return render(request, 'example/home.html', {'form': form})
return render(request, 'example/home.html', context)
17 changes: 8 additions & 9 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from setuptools import find_packages, setup

version = '0.2.1'
version = '0.2.2'

if sys.argv[-1] == 'tag':
print("Tagging the version on github:")
Expand All @@ -21,22 +21,21 @@
url='https://github.com/furious-luke/django-address',
description='A django application for describing addresses.',
long_description=open(os.path.join(os.path.dirname(__file__), 'README.md')).read(),
long_description_content_type='text/markdown',
classifiers=[
'Development Status :: 3 - Alpha',
'Framework :: Django',
'Framework :: Django :: 1.7',
'Framework :: Django :: 1.8',
'Framework :: Django :: 2.2',
'Framework :: Django :: 3.0',
'Intended Audience :: Developers',
'License :: OSI Approved :: BSD License',
'Natural Language :: English',
'Operating System :: OS Independent',
'Programming Language :: Python',
'Programming Language :: Python :: 2',
'Programming Language :: Python :: 2.7',
'Programming Language :: Python :: 3',
'Programming Language :: Python :: 3.3',
'Programming Language :: Python :: 3.4',
'Programming Language :: Python :: 3.5'
'Programming Language :: Python :: 3.5',
'Programming Language :: Python :: 3.6',
'Programming Language :: Python :: 3.7',
'Programming Language :: Python :: 3.8',
],
license='BSD',

Expand Down

0 comments on commit c5df849

Please sign in to comment.