Recently I added django-allauth to my site, to enable logging in with third party auth providers (such as Google).
Allauth docuemntation
As per the installation instructions allauth recommends the following:
- Add a
Site
for your domain, matchingsettings.SITE_ID
(django.contrib.sites
app). - For each OAuth based provider, either add a
SocialApp
(socialaccount
app) containing the required client credentials, or, make sure that these are configured via theSOCIALACCOUNT_PROVIDERS[<provider>]['APP']
setting (see example above).
Django documentation
According to the Django documentation
django.contrib.sites
registers a post_migrate
signal handler which creates
a default site named example.com
with the domain example.com
. This site
will also be created after Django creates the test database. To set the correct
name and domain for your project, you can use a data migration.
The problem
The default example.com
site isn't particularly useful default when working
with the allauth
package.
This configuration requires you to manually head into the admin to:
- Create a new site for localhost:8000
(or however you're developing locally)
- Create a new SocialApp
for each provider (google
in my case) along with
respective secrets, then link to your created site
This is required every time you have to bootstrap your environment, i.e. when cloning the project on a new machine, or after wiping your DB.
I wanted a mechanism to be able to quickly bootstrap my project during development across machines with minimal manual intervention.
The docs indicate a migration can be used to set a correct name and domain for your project but doesn't specify how.
Solution
After a bit of googling I came up with:
from django.conf import settings
from django.contrib.sites.models import Site
from django.db import migrations
def create_site(apps, schema_editor):
if settings.DEBUG:
site = Site.objects.create(name="localhost", domain="localhost:8000")
class Migration(migrations.Migration):
dependencies = [
("feeds", "0001_initial"),
("sites", "0002_alter_domain_unique"),
]
operations = [migrations.RunPython(create_site)]
feeds
is the name of the app I'm developing, and sites
refers to the
builtin django.contrib.sites
app (added to INSTALLED_APPS
). By specifying
these as dependencies this ensures the migration is ran only once these have
completed.
Note it's possible to manually create blank migrations on a per app basis (not from models) with:
./manage.py makemigrations <app> --empty
This alone isn't sufficient to bootstrap a working project with
allauth. Allauth still requires an additional SocialApp
integration to be
created and linked to your site.
Fortunately this is fairly trivial to extend:
import os
from allauth.socialaccount.models import SocialApp
from django.conf import settings
from django.contrib.sites.models import Site
from django.db import migrations
def create_site(apps, schema_editor):
if settings.DEBUG:
site = Site.objects.create(name="localhost", domain="localhost:8000")
social_app = SocialApp.objects.create(
provider="google",
name="google",
client_id=os.environ.get("GOOGLE_CLIENT_ID"),
secret=os.environ.get("GOOGLE_CLIENT_SECRET"),
)
social_app.sites.set([site])
class Migration(migrations.Migration):
dependencies = [
("feeds", "0001_initial"),
("sites", "0002_alter_domain_unique"),
("socialaccount", "0001_initial"),
]
operations = [migrations.RunPython(create_site)]
By storing the secrets as environment variables I can provide a secrets.env
file (that's ignored by version control) with the following values:
GOOGLE_CLIENT_ID="****"
GOOGLE_CLIENT_SECRET="****"
If I clone the project on a new machine (or want to wipe my DB volume and start over) I can quickly bootstrap my project with:
$ ./manage.py makemigrations
$ ./manage.py migrate
And Google login (via allauth) should work out of the box.