├── core ├── __init__.py ├── wsgi.py ├── urls.py └── settings.py ├── notebook ├── migrations │ ├── __init__.py │ ├── 0002_auto_20170821_1235.py │ └── 0001_initial.py ├── __init__.py ├── static │ ├── img │ │ └── logo-algolia.png │ └── css │ │ └── style.css ├── urls.py ├── admin.py ├── apps.py ├── index.py ├── views.py ├── tests.py ├── models.py └── templates │ └── notebook │ ├── _base.html │ ├── auto_complete.html │ └── instant_search.html ├── requirements.txt ├── auto-complete.png ├── instant-search.png ├── manage.py ├── .gitignore └── README.md /core/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /notebook/migrations/__init__.py: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | algoliasearch-django>=1.3.2 2 | -------------------------------------------------------------------------------- /notebook/__init__.py: -------------------------------------------------------------------------------- 1 | default_app_config = 'notebook.apps.NotebookConfig' 2 | -------------------------------------------------------------------------------- /auto-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/algoliasearch-django-example/HEAD/auto-complete.png -------------------------------------------------------------------------------- /instant-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/algoliasearch-django-example/HEAD/instant-search.png -------------------------------------------------------------------------------- /notebook/static/img/logo-algolia.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/algolia/algoliasearch-django-example/HEAD/notebook/static/img/logo-algolia.png -------------------------------------------------------------------------------- /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", "core.settings") 7 | 8 | from django.core.management import execute_from_command_line 9 | 10 | execute_from_command_line(sys.argv) 11 | -------------------------------------------------------------------------------- /notebook/urls.py: -------------------------------------------------------------------------------- 1 | from django.conf.urls import url 2 | from . import views 3 | 4 | 5 | urlpatterns = [ 6 | url(r'^$', views.index, name='index'), 7 | url(r'^auto-complete$', views.auto_complete, name='auto_complete'), 8 | url(r'^instant-search$', views.instant_search, name='instant_search') 9 | ] 10 | 11 | app_name = 'notebook' -------------------------------------------------------------------------------- /notebook/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Contact 3 | 4 | 5 | class ContactAdmin(admin.ModelAdmin): 6 | list_display = ('name', 'company', 'email', 'city', 'followers', 7 | 'updated_at') 8 | list_filter = ['updated_at'] 9 | search_fields = ['name', 'company', 'city'] 10 | 11 | 12 | admin.site.register(Contact, ContactAdmin) 13 | -------------------------------------------------------------------------------- /notebook/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | import algoliasearch_django as algoliasearch 3 | from core import settings 4 | 5 | from .index import ContactIndex 6 | 7 | 8 | class NotebookConfig(AppConfig): 9 | name = 'notebook' 10 | 11 | def ready(self): 12 | Contact = self.get_model('Contact') 13 | if not settings.TESTING: 14 | algoliasearch.register(Contact, ContactIndex) -------------------------------------------------------------------------------- /core/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for core 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.8/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", "core.settings") 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /notebook/index.py: -------------------------------------------------------------------------------- 1 | from algoliasearch_django import AlgoliaIndex 2 | 3 | 4 | class ContactIndex(AlgoliaIndex): 5 | fields = ('name', 'email', 'company', 'address', 'city', 'county', 6 | 'state', 'zip_code', 'phone', 'fax', 'web', 'followers', 'account_names', 'account_ids') 7 | 8 | settings = { 9 | 'searchableAttributes': ['name', 'email', 'company', 'city', 'county', 'account_names', 10 | 'unordered(address)', 'state', 'zip_code', 'phone', 'fax', 11 | 'unordered(web)'], 12 | 'attributesForFaceting': ['city', 'company'], 13 | 'customRanking': ['desc(followers)'], 14 | 'queryType': 'prefixAll', 15 | 'highlightPreTag': '', 16 | 'highlightPostTag': '', 17 | 'hitsPerPage': 15 18 | } 19 | -------------------------------------------------------------------------------- /notebook/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render, redirect 2 | from django.conf import settings 3 | from algoliasearch_django import get_adapter 4 | from .models import Contact 5 | 6 | def index(request): 7 | return redirect('notebook:instant_search', permanent=True) 8 | 9 | def auto_complete(request): 10 | context = { 11 | 'appID': settings.ALGOLIA['APPLICATION_ID'], 12 | 'searchKey': settings.ALGOLIA['SEARCH_API_KEY'], 13 | 'indexName': get_adapter(Contact).index_name 14 | } 15 | return render(request, 'notebook/auto_complete.html', context) 16 | 17 | def instant_search(request): 18 | context = { 19 | 'appID': settings.ALGOLIA['APPLICATION_ID'], 20 | 'searchKey': settings.ALGOLIA['SEARCH_API_KEY'], 21 | 'indexName': get_adapter(Contact).index_name 22 | } 23 | return render(request, 'notebook/instant_search.html', context) 24 | -------------------------------------------------------------------------------- /core/urls.py: -------------------------------------------------------------------------------- 1 | """core URL Configuration 2 | 3 | The `urlpatterns` list routes URLs to views. For more information please see: 4 | https://docs.djangoproject.com/en/1.8/topics/http/urls/ 5 | Examples: 6 | Function views 7 | 1. Add an import: from my_app import views 8 | 2. Add a URL to urlpatterns: url(r'^$', views.home, name='home') 9 | Class-based views 10 | 1. Add an import: from other_app.views import Home 11 | 2. Add a URL to urlpatterns: url(r'^$', Home.as_view(), name='home') 12 | Including another URLconf 13 | 1. Add an import: from blog import urls as blog_urls 14 | 2. Add a URL to urlpatterns: url(r'^blog/', include(blog_urls)) 15 | """ 16 | from django.conf.urls import include, url 17 | from django.contrib import admin 18 | 19 | 20 | urlpatterns = [ 21 | url(r'^', include('notebook.urls', namespace='notebook')), 22 | url(r'^admin/', admin.site.urls), 23 | ] 24 | -------------------------------------------------------------------------------- /notebook/migrations/0002_auto_20170821_1235.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | # Generated by Django 1.11.2 on 2017-08-21 12:35 3 | from __future__ import unicode_literals 4 | 5 | from django.db import migrations, models 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | dependencies = [ 11 | ('notebook', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Account', 17 | fields=[ 18 | ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('username', models.CharField(max_length=40)), 20 | ('service', models.CharField(max_length=40)), 21 | ], 22 | ), 23 | migrations.AddField( 24 | model_name='contact', 25 | name='accounts', 26 | field=models.ManyToManyField(to='notebook.Account'), 27 | ), 28 | ] 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | 5 | # C extensions 6 | *.so 7 | 8 | # Distribution / packaging 9 | .Python 10 | env/ 11 | build/ 12 | develop-eggs/ 13 | dist/ 14 | downloads/ 15 | eggs/ 16 | .eggs/ 17 | lib/ 18 | lib64/ 19 | parts/ 20 | sdist/ 21 | var/ 22 | *.egg-info/ 23 | .installed.cfg 24 | *.egg 25 | 26 | # PyInstaller 27 | # Usually these files are written by a python script from a template 28 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 29 | *.manifest 30 | *.spec 31 | 32 | # Installer logs 33 | pip-log.txt 34 | pip-delete-this-directory.txt 35 | 36 | # Unit test / coverage reports 37 | htmlcov/ 38 | .tox/ 39 | .coverage 40 | .coverage.* 41 | .cache 42 | nosetests.xml 43 | coverage.xml 44 | *,cover 45 | 46 | # Translations 47 | *.mo 48 | *.pot 49 | 50 | # Django stuff: 51 | *.log 52 | 53 | # Sphinx documentation 54 | docs/_build/ 55 | 56 | # PyBuilder 57 | target/ 58 | 59 | *.sqlite3 60 | 61 | # IDE Files 62 | *.iml 63 | .idea/ 64 | -------------------------------------------------------------------------------- /notebook/static/css/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding-top: 70px; 3 | } 4 | 5 | #hits-stat a { 6 | display: inline-block; 7 | background: #23282D; 8 | padding: 1px 3px; 9 | } 10 | 11 | #hits-stat img { 12 | height: 1em; 13 | } 14 | 15 | .mark, mark { 16 | padding: 0; 17 | } 18 | 19 | .twitter-typeahead { 20 | width: 100%; 21 | } 22 | 23 | .twitter-typeahead .tt-input, .twitter-typeahead .tt-hint { 24 | width: 100%; 25 | margin: 0px; 26 | padding: 8px 12px; 27 | outline: none; 28 | } 29 | 30 | .twitter-typeahead .tt-input:focus { 31 | border: 2px solid #0097cf; 32 | } 33 | 34 | .twitter-typeahead .tt-hint { 35 | color: #999; 36 | } 37 | 38 | .twitter-typeahead .tt-dropdown-menu { 39 | width: 100%; 40 | padding: 0; 41 | background-color: #fff; 42 | border: 1px solid rgba(0, 0, 0, 0.2); 43 | border-top: 0px; 44 | } 45 | 46 | .twitter-typeahead .tt-dropdown-menu .tt-suggestion { 47 | text-align: left; 48 | padding: 3px 20px; 49 | font-size: 18px; 50 | line-height: 24px; 51 | } 52 | 53 | .twitter-typeahead .tt-dropdown-menu .tt-suggestion.tt-cursor { 54 | color: #fff; 55 | background-color: #0097cf; 56 | } 57 | -------------------------------------------------------------------------------- /notebook/tests.py: -------------------------------------------------------------------------------- 1 | from unittest import TestCase 2 | from . import models 3 | 4 | 5 | class ContactTestCase(TestCase): 6 | def setUp(self): 7 | contacts = models.Contact.objects.all() 8 | for c in contacts: 9 | c.delete() 10 | self.contact = models.Contact.objects.create(name="foo", email="foo@algolia.com") 11 | self.account1 = models.Account.objects.create(username="test@x.com", service="X") 12 | self.account2 = models.Account.objects.create(username="test@y.com", service="Y") 13 | self.contact.accounts.add(self.account1, self.account2) 14 | 15 | def test_Contacts_account_ids(self): 16 | """Contacts expose the correct account ids """ 17 | foo = models.Contact.objects.get(name="foo") 18 | ids = foo.account_ids() 19 | self.assertEqual(len(ids), 2) 20 | self.assertTrue(self.account1.id in ids) 21 | self.assertTrue(self.account2.id in ids) 22 | 23 | def test_Contacts_account_names(self): 24 | """Contacts expose the correct account names """ 25 | foo = models.Contact.objects.get(name="foo") 26 | names = foo.account_names() 27 | self.assertEqual(len(names), 2) 28 | self.assertTrue('test@x.com@X' in names) 29 | self.assertTrue('test@y.com@Y' in names) 30 | -------------------------------------------------------------------------------- /notebook/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | 3 | 4 | class Account(models.Model): 5 | username = models.CharField(max_length=40) 6 | service = models.CharField(max_length=40) 7 | 8 | def __str__(self): 9 | return self.username + "@" + self.service 10 | 11 | 12 | class Contact(models.Model): 13 | name = models.CharField(max_length=40) 14 | email = models.EmailField(max_length=60) 15 | company = models.CharField(max_length=40) 16 | address = models.CharField(max_length=60) 17 | city = models.CharField(max_length=20) 18 | county = models.CharField(max_length=20) 19 | state = models.CharField(max_length=20) 20 | zip_code = models.CharField(max_length=8) 21 | phone = models.CharField(max_length=20) 22 | fax = models.CharField(max_length=20) 23 | web = models.CharField(max_length=30) 24 | followers = models.IntegerField(default=0) 25 | note = models.TextField() 26 | created_at = models.DateTimeField(auto_now=False, auto_now_add=True) 27 | updated_at = models.DateTimeField(auto_now=True) 28 | 29 | accounts = models.ManyToManyField(Account) 30 | 31 | def __str__(self): 32 | return self.name 33 | 34 | def account_names(self): 35 | return [str(account) for account in self.accounts.all()] 36 | 37 | def account_ids(self): 38 | return [account.id for account in self.accounts.all()] 39 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AlgoliaSearch Django Example 2 | 3 | This is a Django application indexing 500 `Contact` objects and providing auto-completion and instant-search samples. See [algoliasearch-django](https://github.com/algolia/algoliasearch-django) package. 4 | 5 | A `Contact` is defined by: 6 | 7 | * A name 8 | * An email address 9 | * A company name 10 | 11 | An [Algolia](https://www.algolia.com) account is required to test it. 12 | 13 | ## Dependencies 14 | 15 | ```bash 16 | $ pip install algoliasearch-django 17 | ``` 18 | 19 | ## Installation 20 | 21 | ```bash 22 | $ git clone https://github.com/algolia/algoliasearch-django-example 23 | $ cd algoliasearch-django-example 24 | $ python manage.py migrate 25 | $ python manage.py createsuperuser 26 | ``` 27 | 28 | ## Configure the credentials 29 | 30 | You can add your credentials in `core/settings.py` or you can export them in your environment: 31 | 32 | ```bash 33 | $ export ALGOLIA_APPLICATION_ID=XXXXX 34 | $ export ALGOLIA_API_KEY=XXXXX 35 | $ export ALGOLIA_SEARCH_API_KEY=XXXXX 36 | ``` 37 | 38 | ## Populate the DB and start indexing 39 | 40 | ```bash 41 | $ python manage.py loaddata contacts.json 42 | $ python manage.py algolia_reindex 43 | ``` 44 | 45 | ## Start the application 46 | 47 | ```bash 48 | $ python manage.py runserver 49 | ``` 50 | 51 | Enjoy your `http://localhost:8000` examples! 52 | 53 |  54 | 55 |  56 | -------------------------------------------------------------------------------- /notebook/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | from __future__ import unicode_literals 3 | 4 | from django.db import models, migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | ] 11 | 12 | operations = [ 13 | migrations.CreateModel( 14 | name='Contact', 15 | fields=[ 16 | ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), 17 | ('name', models.CharField(max_length=40)), 18 | ('email', models.EmailField(max_length=60)), 19 | ('company', models.CharField(max_length=40)), 20 | ('address', models.CharField(max_length=60)), 21 | ('city', models.CharField(max_length=20)), 22 | ('county', models.CharField(max_length=20)), 23 | ('state', models.CharField(max_length=20)), 24 | ('zip_code', models.CharField(max_length=8)), 25 | ('phone', models.CharField(max_length=20)), 26 | ('fax', models.CharField(max_length=20)), 27 | ('web', models.CharField(max_length=30)), 28 | ('followers', models.IntegerField(default=0)), 29 | ('note', models.TextField()), 30 | ('created_at', models.DateTimeField(auto_now_add=True)), 31 | ('updated_at', models.DateTimeField(auto_now=True)), 32 | ], 33 | ), 34 | ] 35 | -------------------------------------------------------------------------------- /notebook/templates/notebook/_base.html: -------------------------------------------------------------------------------- 1 | {% load static %} 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 |A modern auto-complete field based on typeahead.js + Hogan and AlgoliaSearch JS Client example.
A Google-like instant-search based on AlgoliaSearch JS Client + Hogan example.