S128 adalah salah satu platform sabung ayam online paling populer di Asia. Melalui S128, para penggemar sabung ayam bisa menikmati pertandingan yang disiarkan secara langsung dan bertaruh dengan mudah. Namun, untuk memaksimalkan pengalaman bermain, penting untuk memahami aturan dan strategi yang tepat.
Langkah pertama dalam bermain di S128 adalah mendaftar dan membuat akun. Proses pendaftaran dapat dilakukan melalui agen resmi yang telah memiliki lisensi terpercaya. Saat mendaftar, Anda akan diminta untuk mengisi beberapa informasi pribadi dan melakukan verifikasi akun.
Setelah berhasil membuat akun, Anda perlu melakukan deposit untuk memasang taruhan. S128 menyediakan berbagai metode pembayaran, mulai dari transfer bank hingga e-wallet. Pastikan Anda mengetahui batas minimal deposit yang ditetapkan oleh platform.
Di S128, ada beberapa jenis taruhan yang bisa Anda pilih. Mengetahui perbedaan antara masing-masing jenis taruhan akan membantu Anda dalam menentukan strategi taruhan yang tepat.
Sebelum memasang taruhan, sangat penting untuk melakukan analisis terhadap ayam yang akan bertarung. Lihat kondisi fisik, riwayat pertarungan, dan statistik kemenangan sebelumnya. Dengan begitu, Anda bisa membuat keputusan yang lebih tepat.
Manajemen modal adalah salah satu kunci sukses dalam bermain S128. Jangan memasang taruhan lebih dari yang Anda mampu, dan selalu tetapkan batasan harian baik untuk kemenangan maupun kerugian.
Salah satu fitur unggulan dari S128 adalah siaran langsung pertandingan sabung ayam. Fitur ini memungkinkan pemain untuk menyaksikan pertarungan secara real-time dan membuat analisis berdasarkan performa ayam dalam pertandingan.
Banyak agen S128 yang menawarkan bonus dan promosi kepada pemain baru maupun pemain setia. Manfaatkan bonus deposit atau promo cashback untuk menambah saldo taruhan Anda.
Meskipun sabung ayam bisa sangat menyenangkan, penting untuk bermain dengan bijak. Jangan terbawa emosi saat bermain, dan selalu ingat bahwa ini adalah bentuk hiburan, bukan cara untuk mendapatkan keuntungan instan.
The post Panduan Bermain S128: Tips dan Strategi Menang Sabung Ayam Online appeared first on S128 Situs Sabung Ayam Online Terpercaya .
Sudah berhasil memenangkan taruhan sabung ayam di S128? Selamat! Sekarang saatnya untuk melakukan withdraw atau penarikan dana. Proses withdraw di S128 sangat mudah dan cepat. Berikut langkah-langkah untuk menarik kemenangan Anda.
Langkah pertama adalah login ke akun Anda di S128. Gunakan username dan password yang sudah Anda buat saat pendaftaran.
Setelah masuk, klik pada menu “Withdraw” atau “Penarikan Dana” yang ada di dashboard utama akun Anda. Di menu ini, Anda bisa mulai mengatur penarikan dana.
Tentukan jumlah uang yang ingin Anda tarik. Pastikan jumlah tersebut tidak melebihi saldo yang Anda miliki di akun S128. Periksa juga batas minimal withdraw yang ditetapkan oleh platform.
Pilih metode penarikan yang sesuai, misalnya melalui transfer bank atau metode lain yang disediakan oleh S128. Masukkan data rekening bank yang akan menerima dana.
Setelah semua data terisi dengan benar, klik “Konfirmasi” untuk menyelesaikan proses penarikan. Pastikan semua informasi yang Anda masukkan sudah benar.
Setelah konfirmasi, pihak S128 akan memproses permintaan withdraw Anda. Proses ini biasanya memakan waktu beberapa menit hingga beberapa jam, tergantung metode yang digunakan. Dana akan masuk ke rekening Anda setelah proses selesai.
Cara withdraw di S128 sangat mudah dan tidak memerlukan banyak waktu. Dengan panduan ini, Anda bisa dengan cepat menarik kemenangan Anda dan menikmati hasil taruhan sabung ayam. Pastikan untuk mengikuti langkah-langkah dengan benar agar penarikan berjalan lancar.
The post Cara Withdraw S128: Panduan Cepat untuk Menarik Kemenangan Anda appeared first on S128 Situs Sabung Ayam Online Terpercaya .
Apakah Anda penggemar sabung ayam online dan ingin mencoba peruntungan di S128? Jangan khawatir, proses daftar di S128 sangat mudah dan cepat. Di artikel ini, saya akan membimbing Anda langkah demi langkah cara daftar S128 agar Anda bisa segera bergabung dan menikmati taruhan live.
Langkah pertama dalam cara daftar S128 adalah mengakses situs resmi atau link alternatif yang tersedia. Pastikan Anda menggunakan link yang terpercaya agar bisa mengakses situs tanpa kendala.
Setelah Anda berada di halaman utama S128, cari tombol “Daftar” atau “Registrasi” yang biasanya ada di bagian atas halaman. Tombol ini adalah pintu masuk untuk memulai proses registrasi di S128.
Langkah selanjutnya dalam cara daftar S128 adalah mengisi formulir pendaftaran. Anda perlu memasukkan beberapa informasi penting seperti:
Pastikan semua data yang Anda masukkan benar dan sesuai, karena ini akan berpengaruh pada proses verifikasi akun dan transaksi nantinya.
Setelah formulir selesai diisi, Anda akan menerima email atau SMS untuk verifikasi akun. Klik tautan yang dikirimkan untuk mengaktifkan akun Anda. Proses verifikasi ini penting agar akun Anda aman dan siap digunakan.
Setelah akun berhasil diverifikasi, Anda bisa langsung login menggunakan username dan password yang telah dibuat. Setelah itu, lakukan deposit untuk memulai taruhan sabung ayam online di S128. Anda juga bisa memanfaatkan bonus untuk member baru yang biasanya ditawarkan.
Dengan mengikuti panduan ini, cara daftar S128 akan terasa sangat mudah dan cepat. Anda hanya perlu beberapa langkah sederhana untuk bisa bergabung dan mulai menikmati serunya taruhan sabung ayam online. Segera daftar sekarang dan nikmati berbagai promo menarik serta bonus eksklusif untuk member baru di S128!
The post Tutorial Lengkap Cara Daftar Sabung Ayam Online di S128 appeared first on S128 Situs Sabung Ayam Online Terpercaya .
Sudah siap memulai taruhan sabung ayam online di S128? Langkah selanjutnya setelah mendaftar adalah melakukan deposit. Tenang, proses deposit di S128 sangat mudah dan cepat! Berikut panduan lengkap cara deposit di S128 tanpa ribet.
Pertama, login ke akun Anda di S128 dengan username dan password yang sudah dibuat. Setelah masuk, Anda akan langsung melihat menu utama.
Di dashboard akun, klik menu “Deposit”. Menu ini biasanya terlihat jelas dan mudah diakses. Di sini, Anda akan melihat berbagai opsi pembayaran yang bisa Anda pilih.
S128 menyediakan beberapa metode pembayaran yang bisa Anda gunakan, seperti:
Pilih metode yang paling nyaman untuk Anda.
Selanjutnya, masukkan jumlah uang yang ingin Anda depositkan. Periksa batas minimal deposit yang berlaku sesuai dengan metode yang Anda pilih.
Setelah memasukkan jumlah deposit, lanjutkan dengan menyelesaikan pembayaran sesuai instruksi. Jika Anda menggunakan transfer bank, pastikan Anda mengonfirmasi pembayaran melalui akun Anda.
Setelah melakukan pembayaran, tunggu beberapa saat hingga deposit Anda dikonfirmasi. Saldo Anda akan otomatis bertambah dan siap digunakan untuk bertaruh di S128.
Cara deposit S128 sangat mudah, cepat, dan aman. Dengan berbagai metode pembayaran yang tersedia, Anda bisa memilih yang paling sesuai dengan kebutuhan. Segera lakukan deposit dan nikmati pengalaman taruhan sabung ayam online di S128 dengan layanan terbaik!
The post Cara Deposit S128: Cepat, Aman, dan Tanpa Ribet! appeared first on S128 Situs Sabung Ayam Online Terpercaya .
In this tutorial I’m going to show you how I usually start and organize a new Django project nowadays. I’ve tried many different configurations and ways to organize the project, but for the past 4 years or so this has been consistently my go-to setup.
Please note that this is not intended to be a “best practice” guide or to fit every use case. It’s just the way I like to use Django and that’s also the way that I found that allow your project to grow in healthy way.
Index
Usually those are the premises I take into account when setting up a project:
Usually I work with three environment dimensions in my code: local, tests and production. I like to see it
as a “mode” how I run the project. What dictates which mode I’m running the project is which settings.py I’m currently
using.
The local dimension always come first. It is the settings and setup that a developer will use on their local machine.
All the defaults and configurations must be done to attend the local development environment first.
The reason why I like to do it that way is that the project must be as simple as possible for a new hire to clone the repository, run the project and start coding.
The production environment usually will be configured and maintained by experienced developers and by those who are more familiar with the code base itself. And because the deployment should be automated, there is no reason for people being re-creating the production server over and over again. So it is perfectly fine for the production setup require a few extra steps and configuration.
The tests environment will be also available locally, so developers can test the code and run the static checks.
But the idea of the tests environment is to expose it to a CI environment like Travis CI, Circle CI, AWS Code Pipeline, etc.
It is a simple setup that you can install the project and run all the unit tests.
The production dimension is the real deal. This is the environment that goes live without the testing and debugging utilities.
I also use this “mode” or dimension to run the staging server.
A staging server is where you roll out new features and bug fixes before applying to the production server.
The idea here is that your staging server should run in production mode, and the only difference is going to be your static/media server and database server. And this can be achieved just by changing the configuration to tell what is the database connection string for example.
But the main thing is that you should not have any conditional in your code that checks if it is the production or staging server. The project should run exactly in the same way as in production.
Right from the beginning it is a good idea to setup a remote version control service. My go-to option is Git on GitHub. Usually I create the remote repository first then clone it on my local machine to get started.
Let’s say our project is called simple, after creating the repository on GitHub I will create a directory named
simple on my local machine, then within the simple directory I will clone the repository, like shown on the
structure below:
simple/
└── simple/ (git repo)Then I create the virtualenv outside of the Git repository:
simple/
├── simple/
└── venv/Then alongside the simple and venv directories I may place some other support files related to the project which I
do not plan to commit to the Git repository.
The reason I do that is because it is more convenient to destroy and re-create/re-clone both the virtual environment or the repository itself.
It is also good to store your virtual environment outside of the git repository/project root so you don’t need to bother ignoring its path when using libs like flake8, isort, black, tox, etc.
You can also use tools like virtualenvwrapper to manage your virtual environments, but I prefer doing it that way
because everything is in one place. And if I no longer need to keep a given project on my local machine, I can delete
it completely without leaving behind anything related to the project on my machine.
The next step is installing Django inside the virtualenv so we can use the django-admin commands.
source venv/bin/activate
pip install djangoInside the simple directory (where the git repository was cloned) start a new project:
django-admin startproject simple .Attention to the . in the end of the command. It is necessary to not create yet another directory called simple.
So now the structure should be something like this:
simple/ <- (1) Wrapper directory with all project contents including the venv
├── simple/ <- (2) Project root and git repository
│ ├── .git/
│ ├── manage.py
│ └── simple/ <- (3) Project package, apps, templates, static, etc
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── venv/At this point I already complement the project package directory with three extra directories for templates, static
and locale.
Both templates and static we are going to manage at a project-level and app-level. Those are refer to the global
templates and static files.
The locale is necessary in case you are using i18n to translate your application to other languages. So here
is where you are going to store the .mo and .po files.
So the structure now should be something like this:
simple/
├── simple/
│ ├── .git/
│ ├── manage.py
│ └── simple/
│ ├── locale/
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── venv/Inside the project root (2) I like to create a directory called requirements with all the .txt files, breaking down
the project dependencies like this:
base.txt: Main dependencies, strictly necessary to make the project run. Common to all environmentstests.txt: Inherits from base.txt + test utilitieslocal.txt: Inherits from tests.txt + development utilitiesproduction.txt: Inherits from base.txt + production only dependenciesNote that I do not have a staging.txt requirements file, that’s because the staging environment is going to use the
production.txt requirements so we have an exact copy of the production environment.
simple/
├── simple/
│ ├── .git/
│ ├── manage.py
│ ├── requirements/
│ │ ├── base.txt
│ │ ├── local.txt
│ │ ├── production.txt
│ │ └── tests.txt
│ └── simple/
│ ├── locale/
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── asgi.py
│ ├── settings.py
│ ├── urls.py
│ └── wsgi.py
└── venv/Now let’s have a look inside each of those requirements file and what are the python libraries that I always use no matter what type of Django project I’m developing.
base.txt
dj-database-url==0.5.0
Django==3.2.4
psycopg2-binary==2.9.1
python-decouple==3.4
pytz==2021.1.env files in a safe waysettings.py module. It also helps with decoupling configuration from source codetests.txt
-r base.txt
black==21.6b0
coverage==5.5
factory-boy==3.2.0
flake8==3.9.2
isort==5.9.1
tox==3.23.1The -r base.txt inherits all the requirements defined in the base.txt file
local.txt
-r tests.txt
django-debug-toolbar==3.2.1
ipython==7.25.0The -r tests.txt inherits all the requirements defined in the base.txt and tests.txt file
production.txt
-r base.txt
gunicorn==20.1.0
sentry-sdk==1.1.0The -r base.txt inherits all the requirements defined in the base.txt file
Also following the environments and modes premise I like to setup multiple settings modules. Those are going to serve as the entry point to determine in which mode I’m running the project.
Inside the simple project package, I create a new directory called settings and break down the files like this:
simple/ (1)
├── simple/ (2)
│ ├── .git/
│ ├── manage.py
│ ├── requirements/
│ │ ├── base.txt
│ │ ├── local.txt
│ │ ├── production.txt
│ │ └── tests.txt
│ └── simple/ (3)
│ ├── locale/
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── local.py
│ │ ├── production.py
│ │ └── tests.py
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── asgi.py
│ ├── urls.py
│ └── wsgi.py
└── venv/Note that I removed the settings.py that used to live inside the simple/ (3) directory.
The majority of the code will live inside the base.py settings module.
Everything that we can set only once in the base.py and change its value using python-decouple we should keep in the
base.py and never repeat/override in the other settings modules.
After the removal of the main settings.py a nice touch is to modify the manage.py file to set the
local.py as the default settings module so we can still run commands like python manage.py runserver without any
further parameters:
manage.py
#!/usr/bin/env python
"""Django's command-line utility for administrative tasks."""
import os
import sys
def main():
"""Run administrative tasks."""
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'simple.settings.local') # <- here!
try:
from django.core.management import execute_from_command_line
except ImportError as exc:
raise ImportError(
"Couldn't import Django. Are you sure it's installed and "
"available on your PYTHONPATH environment variable? Did you "
"forget to activate a virtual environment?"
) from exc
execute_from_command_line(sys.argv)
if __name__ == '__main__':
main()Now let’s have a look on each of those settings modules.
base.py
from pathlib import Path
import dj_database_url
from decouple import Csv, config
BASE_DIR = Path(__file__).resolve().parent.parent
# ==============================================================================
# CORE SETTINGS
# ==============================================================================
SECRET_KEY = config("SECRET_KEY", default="django-insecure$simple.settings.local")
DEBUG = config("DEBUG", default=True, cast=bool)
ALLOWED_HOSTS = config("ALLOWED_HOSTS", default="127.0.0.1,localhost", cast=Csv())
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
]
DEFAULT_AUTO_FIELD = "django.db.models.BigAutoField"
ROOT_URLCONF = "simple.urls"
INTERNAL_IPS = ["127.0.0.1"]
WSGI_APPLICATION = "simple.wsgi.application"
# ==============================================================================
# MIDDLEWARE SETTINGS
# ==============================================================================
MIDDLEWARE = [
"django.middleware.security.SecurityMiddleware",
"django.contrib.sessions.middleware.SessionMiddleware",
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
# ==============================================================================
# TEMPLATES SETTINGS
# ==============================================================================
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.debug",
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]
# ==============================================================================
# DATABASES SETTINGS
# ==============================================================================
DATABASES = {
"default": dj_database_url.config(
default=config("DATABASE_URL", default="postgres://simple:simple@localhost:5432/simple"),
conn_max_age=600,
)
}
# ==============================================================================
# AUTHENTICATION AND AUTHORIZATION SETTINGS
# ==============================================================================
AUTH_PASSWORD_VALIDATORS = [
{
"NAME": "django.contrib.auth.password_validation.UserAttributeSimilarityValidator",
},
{
"NAME": "django.contrib.auth.password_validation.MinimumLengthValidator",
},
{
"NAME": "django.contrib.auth.password_validation.CommonPasswordValidator",
},
{
"NAME": "django.contrib.auth.password_validation.NumericPasswordValidator",
},
]
# ==============================================================================
# I18N AND L10N SETTINGS
# ==============================================================================
LANGUAGE_CODE = config("LANGUAGE_CODE", default="en-us")
TIME_ZONE = config("TIME_ZONE", default="UTC")
USE_I18N = True
USE_L10N = True
USE_TZ = True
LOCALE_PATHS = [BASE_DIR / "locale"]
# ==============================================================================
# STATIC FILES SETTINGS
# ==============================================================================
STATIC_URL = "/static/"
STATIC_ROOT = BASE_DIR.parent.parent / "static"
STATICFILES_DIRS = [BASE_DIR / "static"]
STATICFILES_FINDERS = (
"django.contrib.staticfiles.finders.FileSystemFinder",
"django.contrib.staticfiles.finders.AppDirectoriesFinder",
)
# ==============================================================================
# MEDIA FILES SETTINGS
# ==============================================================================
MEDIA_URL = "/media/"
MEDIA_ROOT = BASE_DIR.parent.parent / "media"
# ==============================================================================
# THIRD-PARTY SETTINGS
# ==============================================================================
# ==============================================================================
# FIRST-PARTY SETTINGS
# ==============================================================================
SIMPLE_ENVIRONMENT = config("SIMPLE_ENVIRONMENT", default="local")A few comments on the overall base settings file contents:
config() are from the python-decouple library. It is exposing the configuration to an environment variable and
retrieving its value accordingly to the expected data type. Read more about python-decouple on this guide:
How to Use Python DecoupleSECRET_KEY, DEBUG and ALLOWED_HOSTS defaults to local/development environment values.
That means a new developer won’t need to set a local .env and provide some initial value to run locallydj_database_url to translate this one line string to a Python
dictionary as Django expectsMEDIA_ROOT we are navigating two directories up to create a media directory outside the git
repository but inside our project workspace (inside the directory simple/ (1)). So everything is handy and we won’t
be committing test uploads to our repositorybase.py settings I reserve two blocks for third-party Django libraries that I may install, such
as Django Rest Framework or Django Crispy Forms. And the first-party settings refer to custom settings that I may create
exclusively for our project. Usually I will prefix them with the project name, like SIMPLE_XXXlocal.py
# flake8: noqa
from .base import *
INSTALLED_APPS += ["debug_toolbar"]
MIDDLEWARE.insert(0, "debug_toolbar.middleware.DebugToolbarMiddleware")
# ==============================================================================
# EMAIL SETTINGS
# ==============================================================================
EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend"Here is where I will setup Django Debug Toolbar for example. Or set the email backend to display the sent emails on console instead of having to setup a valid email server to work on the project.
All the code that is only relevant for the development process goes here.
You can use it to setup other libs like Django Silk to run profiling without exposing it to production.
tests.py
# flake8: noqa
from .base import *
PASSWORD_HASHERS = ["django.contrib.auth.hashers.MD5PasswordHasher"]
class DisableMigrations:
def __contains__(self, item):
return True
def __getitem__(self, item):
return None
MIGRATION_MODULES = DisableMigrations()Here I add configurations that help us run the test cases faster. Sometimes disabling the migrations may not work if you have interdependencies between the apps models so Django may fail to create a database without the migrations.
In some projects it is better to keep the test database after the execution.
production.py
# flake8: noqa
import sentry_sdk
from sentry_sdk.integrations.django import DjangoIntegration
import simple
from .base import *
# ==============================================================================
# SECURITY SETTINGS
# ==============================================================================
CSRF_COOKIE_SECURE = True
CSRF_COOKIE_HTTPONLY = True
SECURE_HSTS_SECONDS = 60 * 60 * 24 * 7 * 52 # one year
SECURE_HSTS_INCLUDE_SUBDOMAINS = True
SECURE_SSL_REDIRECT = True
SECURE_BROWSER_XSS_FILTER = True
SECURE_CONTENT_TYPE_NOSNIFF = True
SECURE_PROXY_SSL_HEADER = ("HTTP_X_FORWARDED_PROTO", "https")
SESSION_COOKIE_SECURE = True
# ==============================================================================
# THIRD-PARTY APPS SETTINGS
# ==============================================================================
sentry_sdk.init(
dsn=config("SENTRY_DSN", default=""),
environment=SIMPLE_ENVIRONMENT,
release="simple@%s" % simple.__version__,
integrations=[DjangoIntegration()],
)The most important part here on the production settings is to enable all the security settings Django offer. I like to do it that way because you can’t run the development server with most of those configurations on.
The other thing is the Sentry configuration.
Note the simple.__version__ on the release. Next we are going to explore how I usually manage the version of the
project.
I like to reuse Django’s get_version utility for a simple and PEP 440 complaint version identification.
Inside the project’s __init__.py module:
simple/
├── simple/
│ ├── .git/
│ ├── manage.py
│ ├── requirements/
│ └── simple/
│ ├── locale/
│ ├── settings/
│ ├── static/
│ ├── templates/
│ ├── __init__.py <-- here!
│ ├── asgi.py
│ ├── urls.py
│ └── wsgi.py
└── venv/You can do something like this:
from django import get_version
VERSION = (1, 0, 0, "final", 0)
__version__ = get_version(VERSION)The only down side of using the get_version directly from the Django module is that it won’t be able to resolve the
git hash for alpha versions.
A possible solution is making a copy of the django/utils/version.py file to your project, and then you import it
locally, so it will be able to identify your git repository within the project folder.
But it also depends what kind of versioning you are using for your project. If the version of your project is not really relevant to the end user and you want to keep track of it for internal management like to identify the release on a Sentry issue, you could use a date-based release versioning.
A Django app is a Python package that you “install” using the INSTALLED_APPS in your settings file. An app can live pretty
much anywhere: inside or outside the project package or even in a library that you installed using pip.
Indeed, your Django apps may be reusable on other projects. But that doesn’t mean it should. Don’t let it destroy your project design or don’t get obsessed over it. Also, it shouldn’t necessarily represent a “part” of your website/web application.
It is perfectly fine for some apps to not have models, or other apps have only views. Some of your modules doesn’t even need to be a Django app at all. I like to see my Django projects as a big Python package and organize it in a way that makes sense, and not try to place everything inside reusable apps.
The general recommendation of the official Django documentation is to place your apps in the project root (alongside
the manage.py file, identified here in this tutorial by the simple/ (2) folder).
But actually I prefer to create my apps inside the project package (identified in this tutorial by the simple/ (3)
folder). I create a module named apps and then inside the apps I create my Django apps. The main reason why is that
it creates a nice namespace for the app. It helps you easily identify that a particular import is part of your
project. Also this namespace helps when creating logging rules to handle events in a different way.
Here is an example of how I do it:
simple/ (1)
├── simple/ (2)
│ ├── .git/
│ ├── manage.py
│ ├── requirements/
│ └── simple/ (3)
│ ├── apps/ <-- here!
│ │ ├── __init__.py
│ │ ├── accounts/
│ │ └── core/
│ ├── locale/
│ ├── settings/
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── asgi.py
│ ├── urls.py
│ └── wsgi.py
└── venv/In the example above the folders accounts/ and core/ are Django apps created with the command django-admin startapp.
Those two apps are also always in my project. The accounts app is the one that I use the replace the default Django
User model and also the place where I eventually create password reset, account activation, sign ups, etc.
The core app I use for general/global implementations. For example to define a model that will be used across most
of the other apps. I try to keep it decoupled from other apps, not importing other apps resources. It usually is a good
place to implement general purpose or reusable views and mixins.
Something to pay attention when using this approach is that you need to change the name of the apps configuration,
inside the apps.py file of the Django app:
accounts/apps.py
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'accounts' # <- this is the default name created by the startapp commandYou should rename it like this, to respect the namespace:
from django.apps import AppConfig
class AccountsConfig(AppConfig):
default_auto_field = 'django.db.models.BigAutoField'
name = 'simple.apps.accounts' # <- change to this!Then on your INSTALLED_APPS you are going to create a reference to your models like this:
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"simple.apps.accounts",
"simple.apps.core",
]The namespace also helps to organize your INSTALLED_APPS making your project apps easily recognizable.
This is what my app structure looks like:
simple/ (1)
├── simple/ (2)
│ ├── .git/
│ ├── manage.py
│ ├── requirements/
│ └── simple/ (3)
│ ├── apps/
│ │ ├── accounts/ <- My app structure
│ │ │ ├── migrations/
│ │ │ │ └── __init__.py
│ │ │ ├── static/
│ │ │ │ └── accounts/
│ │ │ ├── templates/
│ │ │ │ └── accounts/
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── factories.py
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── constants.py
│ │ │ ├── models.py
│ │ │ └── views.py
│ │ ├── core/
│ │ └── __init__.py
│ ├── locale/
│ ├── settings/
│ ├── static/
│ ├── templates/
│ ├── __init__.py
│ ├── asgi.py
│ ├── urls.py
│ └── wsgi.py
└── venv/The first thing I do is create a folder named tests so I can break down my tests into several files. I always add a
factories.py to create my model factories using the factory-boy library.
For both static and templates always create first a directory with the same name as the app to avoid name collisions
when Django collect all static files and try to resolve the templates.
The admin.py may be there or not depending if I’m using the Django Admin contrib app.
Other common modules that you may have is a utils.py, forms.py, managers.py, services.py etc.
Now I’m going to show you the configuration that I use for tools like isort, black, flake8, coverage and tox.
The .editorconfig file is a standard recognized by all major IDEs and code editors. It helps the editor understand
what is the file formatting rules used in the project.
It tells the editor if the project is indented with tabs or spaces. How many spaces/tabs. What’s the max length for a line of code.
I like to use Django’s .editorconfig file. Here is what it looks like:
.editorconfig
# https://editorconfig.org/
root = true
[*]
indent_style = space
indent_size = 4
insert_final_newline = true
trim_trailing_whitespace = true
end_of_line = lf
charset = utf-8
# Docstrings and comments use max_line_length = 79
[*.py]
max_line_length = 119
# Use 2 spaces for the HTML files
[*.html]
indent_size = 2
# The JSON files contain newlines inconsistently
[*.json]
indent_size = 2
insert_final_newline = ignore
[**/admin/js/vendor/**]
indent_style = ignore
indent_size = ignore
# Minified JavaScript files shouldn't be changed
[**.min.js]
indent_style = ignore
insert_final_newline = ignore
# Makefiles always use tabs for indentation
[Makefile]
indent_style = tab
# Batch files use tabs for indentation
[*.bat]
indent_style = tab
[docs/**.txt]
max_line_length = 79
[*.yml]
indent_size = 2Flake8 is a Python library that wraps PyFlakes, pycodestyle and Ned Batchelder’s McCabe script. It is a great toolkit for checking your code base against coding style (PEP8), programming errors (like “library imported but unused” and “Undefined name”) and to check cyclomatic complexity.
To learn more about flake8, check this tutorial I posted a while a go: How to Use Flake8.
setup.cfg
[flake8]
exclude = .git,.tox,*/migrations/*
max-line-length = 119isort is a Python utility / library to sort imports alphabetically, and automatically separated into sections.
To learn more about isort, check this tutorial I posted a while a go: How to Use Python isort Library.
setup.cfg
[isort]
force_grid_wrap = 0
use_parentheses = true
combine_as_imports = true
include_trailing_comma = true
line_length = 119
multi_line_output = 3
skip = migrations
default_section = THIRDPARTY
known_first_party = simple
known_django = django
sections=FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDERPay attention to the known_first_party, it should be the name of your project so isort can group your project’s
imports.
Black is a life changing library to auto-format your Python applications. There is no way I’m coding with Python nowadays without using Black.
Here is the basic configuration that I use:
pyproject.toml
[tool.black]
line-length = 119
target-version = ['py38']
include = '\.pyi?$'
exclude = '''
/(
\.eggs
| \.git
| \.hg
| \.mypy_cache
| \.tox
| \.venv
| _build
| buck-out
| build
| dist
| migrations
)/
'''In this tutorial I described my go-to project setup when working with Django. That’s pretty much how I start all my projects nowadays.
Here is the final project structure for reference:
simple/
├── simple/
│ ├── .git/
│ ├── .gitignore
│ ├── .editorconfig
│ ├── manage.py
│ ├── pyproject.toml
│ ├── requirements/
│ │ ├── base.txt
│ │ ├── local.txt
│ │ ├── production.txt
│ │ └── tests.txt
│ ├── setup.cfg
│ └── simple/
│ ├── __init__.py
│ ├── apps/
│ │ ├── accounts/
│ │ │ ├── migrations/
│ │ │ │ └── __init__.py
│ │ │ ├── static/
│ │ │ │ └── accounts/
│ │ │ ├── templates/
│ │ │ │ └── accounts/
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── factories.py
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── constants.py
│ │ │ ├── models.py
│ │ │ └── views.py
│ │ ├── core/
│ │ │ ├── migrations/
│ │ │ │ └── __init__.py
│ │ │ ├── static/
│ │ │ │ └── core/
│ │ │ ├── templates/
│ │ │ │ └── core/
│ │ │ ├── tests/
│ │ │ │ ├── __init__.py
│ │ │ │ └── factories.py
│ │ │ ├── __init__.py
│ │ │ ├── admin.py
│ │ │ ├── apps.py
│ │ │ ├── constants.py
│ │ │ ├── models.py
│ │ │ └── views.py
│ │ └── __init__.py
│ ├── locale/
│ ├── settings/
│ │ ├── __init__.py
│ │ ├── base.py
│ │ ├── local.py
│ │ ├── production.py
│ │ └── tests.py
│ ├── static/
│ ├── templates/
│ ├── asgi.py
│ ├── urls.py
│ └── wsgi.py
└── venv/You can also explore the code on GitHub: django-production-template.
Looking to build their own ergonomic mechanical split keyboard, Gosse Adema turned to the Raspberry Pi Zero W for help.
Gosse has been happily using a Microsoft Natural Elite keyboard for years. You know the sort, they look like this:
Twenty years down the line, the keyboard has seen better days and, when looking for a replacement, Gosse decided to make their own.
This is my the first mechanical keyboard project. And this will be for daily usage. Although the possibilities are almost endless, I limit myself to the basic functionality: An ergonomic keyboard with mouse functions.
While searching for new switched, Gosse came across a low-profile Cherry MX that would allow for a thinner keyboard. And what’s the best device to use when trying to keep the profile of your project as thin as possible? Well, hello there, Raspberry Pi Zero W, aren’t you looking rather svelte today.
After deciding to use a Raspberry Pi as the keyboard controller over other common devices, Gosse took inspiration from an Adafruit tutorial on turning Raspberry Pi into a USB gadget, and from “the usbarmory Github page of Chris Kuethe”, which describes how to create a USB gadget with a keyboard.
There is a lot *A LOT* of information on how Gosse built the keyboard on Instructables and, if we try to go into any detail here, our word count is going to be in the thousands. So, let’s just say this: the project uses some 3D printing, some Python code, and some ingenuity to create a lovely-looking final keyboard. If you want to make your own, Gosse has provided absolutely all the information you need to do so. So check it out, and be sure to give Gosse some love via the comments section on Instructables.
Also, if you’re unsure of how a mechanical keyboard differs from other keyboards, we made this handy video for you all!
How do mechanical keyboards work?
So, what makes a mechanical keyboard ‘mechanical’? And why are some mechanical keyboards more ‘clicky’ than others? Custom PC’s Edward Chester explains all. …
The post Building a split mechanical keyboard with a Raspberry Pi Zero controller appeared first on Raspberry Pi.
People make marvellous things for their pets with Raspberry Pi. Here’s a splendid hamster feeder tutorial from Christopher Barnatt of Explaining Computers, just perfect if you’re after a small project for this weekend.
Raspberry Pi Zero Hamster Feeder
Raspberry Pi servo-controlled pet feeder, using a Raspberry Pi Zero and two SG90 servo motors. This project builds on the servo control code and setup from m…
All you need to build your hamster feeder is a Raspberry Pi Zero and peripherals, a couple of servos, some plasticard, sellotape and liquid polyadhesive, and some jumper wires. The video takes you very clearly through the entire set-up, from measurements to wiring details to Python code (which is available to download). As Christopher explains, this will allow you to feed your hamster controlled portions of food at suitable intervals, so that it doesn’t eat the lot in one go and, consequently, explode. What’s not to love?
Check out the Explaining Computers YouTube channel for more clear, detailed videos to help you do more with computing. And for more Raspberry Pi projects, head to our own Raspberry Pi projects, with hundreds of ideas for beginners and beyond available in English and many other languages.
The post Make a hamster feeder with Raspberry Pi Zero appeared first on Raspberry Pi.
Day three of our Pong celebration leads us here, to HackSpace magazine’s ultrasonic hack of Eben’s Code the Classics Pong tribute, Boing!
If you haven’t yet bought your copy of Code the Classics, you have until 11:59pm GMT tonight to get £1 off using the discount code PONG. Click here to visit the Raspberry Pi Press online store to secure your copy, and read on to see how you can use ultrasonic sensors to turn this classic game into something a lot more physical.
Over to the HackSpace magazine team…
Code the Classics is an entertaining book for a whole bunch of reasons, but one aspect of it that is particularly exciting to us makers is that it means there are some games out there that are really fun to play, but also written to be easy to understand and have high-quality game art to go along with them. Why does this excite us as makers? Because it makes them ideal candidates for testing out novel DIY games controllers!
We’re going to start right at the beginning of the book (and also at the beginning of computer game history) with the game Pong. There’s a great chapter on this seminal game in the book, but we’ll dive straight into the source code of our Boing! tribute game. This code should run on any computer with Python 3 (and a few dependencies) installed, but we’ll use a Raspberry Pi, as this has GPIO pins that we can use to add on our extra controller.
Download the code here by clicking the ‘Clone or download’ button, and then ‘Download ZIP’. Unzip the downloaded file, and you should have a directory called Code‑The‑Classics-master, and inside this, a directory called boing-master.
Open a terminal and navigate to this directory, then run:
python3 boing.py
If everything works well, you’ll get a screen asking you to select one or two players – press SPACE to confirm your selection, and have a play.
So that’s how Eben Upton designed the game to be played. Let’s put our own spin on it. Games controllers are basically just sensors that take input from the real world in some way and translate that into in-game actions. Most commonly, these sensors are buttons that you press, but there’s no need for that to be the case. You can use almost any sensor you can get input from – it sounds trite, but the main limitation really is your imagination!
We were playing with ultrasonic distance sensors in the last issue of HackSpace magazine, and this sprung to mind a Pong controller. After all, distance sensors measure in one dimension and Pong bats travel in one dimension.
Last issue we learned that the main challenge when using the cheap HC-SR04 sensors with 3.3V devices is that they use 5V, so we need to reduce their output to 3.3V. A simple voltage divider does the trick, and we used three 330Ω resistors to achieve this – see Figure 1 for more details.
There’s support for these sensors in the GPIO Zero Python library. As a simple test, you can obtain the distance with the following Python code:
import gpiozero
import time
sensor = gpiozero.DistanceSensor(echo=15,trigger=14)
while True:
print(sensor.distance)
time.sleep(0.1)
That will give you a constant read-out of the distance between the ultrasonic sensor and whatever object is in front of it. If you wave your hand around in front of the sensor, you’ll see the numbers changing from 0 to 1, which is the distance in metres.
So far, so straightforward. We only need to add a few bits to the code of our Boing! game to make it interact with the sensor. You can download an updated version of Boing! here, but the changes are as follows.
Add this line to the import statements at the top:
import gpiozero
Add this line to instantiate the distance sensor object at the end of the file (just before pgzrun.go()):
p1_distance = DistanceSensor(echo=15,trigger=14,queue_len=5)
We added the queue_len parameter to get the distances through a little quicker.
Finally, overwrite the p1_controls function with the following:
def p1_controls():
move = 0
distance = p1_distance.distance
print(distance)
if distance < 0.1:
move = PLAYER_SPEED
elif distance > 0.2:
move = -PLAYER_SPEED
return move
This uses the rather arbitrary settings of 10 cm and 20 cm to define whether the paddle moves up or down. You can adjust these as required.
That’s all there is to our ultrasonic Pong. It’s great fun to play, but there are, no doubt, loads of other versions of this classic game you can make by adding different sensors. Why not see what you can come up with?
Today is the last day to get £1 off Code the Classics with the promo code PONG, so visit the Raspberry Pi Press online store to order your discounted copy before 11:59pm GMT tonight.
You can also download Code the Classics as a free PDF here, but the book, oh, the book – it’s a marvellous publication that deserves a physical presence in your home.
The post Play Pong with ultrasonic sensors and a Raspberry Pi | HackSpace magazine appeared first on Raspberry Pi.
Following on from yesterday’s introduction to Pong, we’re sharing Boing!, the Python-based tribute to Pong created by Eben Upton exclusively for Code the Classics. Read on to get a detailed look at the code for Boing!
You can find the download link for the Boing! code in the Code the Classics book, available now in a variety of formats. Be sure to stick with today’s blog post until the end, for a special Code the Classics offer.
To show how a game like Pong can be coded, we’ve created Boing! using Pygame Zero, a beginner-friendly tool for making games in Python. It’s a good starting point for learning how games work – it takes place on a single screen without any scrolling, there are only three moving objects in the game (two bats and a ball), and the artificial intelligence for the computer player can be very simple – or even non-existent, if you’re happy for the game to be multiplayer only. In this case, we have both single-player and two-player modes.
The code can be divided into three parts. First, there’s the initial startup code. We import from other Python modules so we can use their code from ours. Then we check to make sure that the player has sufficiently up-to-date versions of Python and Pygame Zero. We set the WIDTH and HEIGHT variables, which are used by Pygame Zero when creating the game window. We also create two small helper functions which are used by the code.
The next section is the largest. We create four classes: Impact, Ball, Bat, and Game. The first three classes inherit from Pygame Zero’s Actor class, which amongst other things keeps track of an object’s location in the game world, and takes care of loading and displaying sprites. Bat and Ball define the behaviour of the corresponding objects in the game, while Impact is used for an animation which is displayed briefly whenever the ball bounces off something. The Game class’s job is to create and keep track of the key game objects, such as the two bats and the ball.
Further down, we find the update and draw functions. Pygame Zero calls these each frame, and aims to maintain a frame rate of 60 frames per second. Gameplay logic, such as updating the position of an object or working out if a point has been scored, should go in update, while in draw we tell each of the Actor objects to draw itself, as well as displaying backgrounds, text, and suchlike.
Our update and draw functions make use of two global variables: state and game. At any given moment, the game can be in one of three states: the main menu, playing the game, or the game-over screen. The update and draw functions read the state variable and run only the code relevant to the current state. So if state is currently State.MENU, for example, update checks to see if the SPACE bar or the up/down arrows are pressed and updates the menu accordingly, and draw displays the menu on the screen. The technical term for this kind of system is ‘finite state machine’.
The Game class’s job is to create and keep track of the key game objects
The game variable references an instance of the Game class as described above. The __init__ (constructor) method of Game optionally receives a parameter named controls. When we create a new Game object for the main menu, we don’t provide this parameter and so the game will therefore run in attract mode – in other words, while you’re on the main menu, you’ll see two computer-controlled players playing against each other in the background. When the player chooses to start a new game, we replace the existing Game instance with a new one, initialising it with information about the controls to be used for each player – if the controls for the second player are not specified, this indicates that the player has chosen a single-player game, so the second will be computer-controlled.
In Boing!, the Bat and Ball classes inherit from Pygame Zero’s Actor class, which provides a number of ways to specify an object’s position. In this game, as well as games in later chapters, we’re setting positions using the x and y attributes, which by default specify where the centre of the sprite will be on the screen. Of course, we can’t just set an object’s position at the start and be done with it – if we want it to move as the game progresses, we need to update its position each frame. In the case of a Bat, movement is very simple. Each frame, we check to see if the relevant player (which could be a human or the computer) wants to move – if they do, we either subtract or add 4 from the bat’s Y coordinate, depending on whether they want to move up or down. We also ensure that the bat does not go off the top or bottom of the screen. So, not only are we only moving along a single axis, our Y coordinate will always be an integer (i.e. a whole number). For many games, this kind of simple movement is sufficient. Even in games where an object can move along both the X and Y axes, we can often think of the movement along each axis as being separate. For example, in the next chapter’s game, Cavern, the player might be pressing the right arrow key and therefore moving along the X axis at 4 pixels per frame, while also moving along the Y axis at 10 pixels per frame due to gravity. The movement along each axis is independent of the other.
Able to move at any angle, the ball needs to move at the same speed regardless of its direction
For the Ball, things get a bit more complicated. Not only can it move at any angle, it also needs to move at the same speed regardless of its direction. Imagine the ball moving at one pixel per frame to the right. Now imagine trying to make it move at a 45° angle from that by making it move one pixel right and one pixel up per frame. That’s a longer distance, so it would be moving faster overall. That’s not great, and that’s before we’ve even started to think about movement in any possible direction.
The solution is to make use of vector mathematics and trigonometry. In the context of a 2D game, a vector is simply a pair of numbers: X and Y. There are many ways in which vectors can be used, but most commonly they represent positions or directions.
You’ll notice that the Ball class has a pair of attributes, dx and dy. Together these form a vector representing the direction in which the ball is heading. If dx and dy are 1 and 0.5, then each time the ball moves, it’ll move by one pixel on the X axis and a half a pixel on the Y axis. What does it mean to move half a pixel? When a sprite is drawn, Pygame Zero will round its position to the nearest pixel. So the end result is that our sprite will move down the screen by one pixel every other frame, and one pixel to the right every frame (Figure 1).
We still need to make sure that our object moves at a consistent speed regardless of its direction. What we need to do is ensure that our direction vector is always a ‘unit vector’ – a vector which represents a distance of one (in this case, one means one pixel, but in some games it will represent a different distance, such as one metre). Near the top of the code you’ll notice a function named normalised. This takes a pair of numbers representing a vector, uses Python’s math.hypot function to calculate the length of that vector, and then divides both the X and Y components of the vector by that length, resulting in a vector which points in the same direction but has a length of one (Figure 2).
Vector maths is a big field, and we’ve only scratched the surface here. You can find many tutorials online, and we also recommend checking out the Vector2 class in Pygame (the library on top of which Pygame Zero is built).
Update Raspbian to try Boing! and other Code the Classics games on your Raspberry Pi.
The full BOING! tutorial, including challenges, further explanations, and a link to the downloadable code can be found in Code the Classics, the latest book from Raspberry Pi Press.
We’re offering £1 off Code the Classics if you order it before midnight tomorrow from the Raspberry Pi Press online store. Visit the store now, or use the discount code PONG at checkout if you make a purchase before midnight tomorrow.
As always, Code the Classics is available as a free PDF from the Wireframe website, but we highly recommend purchasing the physical book, as it’s rather lovely to look at and would make a great gift for any gaming and/or coding enthusiast.
The post Create Boing!, our Python tribute to Pong appeared first on Raspberry Pi.
Chart.js is a cool open source JavaScript library that helps you render HTML5 charts. It is responsive and counts with 8 different chart types.
In this tutorial we are going to explore a little bit of how to make Django talk with Chart.js and render some simple charts based on data extracted from our models.
For this tutorial all you are going to do is add the Chart.js lib to your HTML page:
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>You can download it from Chart.js official website and use it locally, or you can use it from a CDN using the URL above.
I’m going to use the same example I used for the tutorial How to Create Group By Queries With Django ORM which is a good complement to this tutorial because actually the tricky part of working with charts is to transform the data so it can fit in a bar chart / line chart / etc.
We are going to use the two models below, Country and City:
class Country(models.Model):
name = models.CharField(max_length=30)
class City(models.Model):
name = models.CharField(max_length=30)
country = models.ForeignKey(Country, on_delete=models.CASCADE)
population = models.PositiveIntegerField()And the raw data stored in the database:
| cities | |||
|---|---|---|---|
| id | name | country_id | population |
| 1 | Tokyo | 28 | 36,923,000 |
| 2 | Shanghai | 13 | 34,000,000 |
| 3 | Jakarta | 19 | 30,000,000 |
| 4 | Seoul | 21 | 25,514,000 |
| 5 | Guangzhou | 13 | 25,000,000 |
| 6 | Beijing | 13 | 24,900,000 |
| 7 | Karachi | 22 | 24,300,000 |
| 8 | Shenzhen | 13 | 23,300,000 |
| 9 | Delhi | 25 | 21,753,486 |
| 10 | Mexico City | 24 | 21,339,781 |
| 11 | Lagos | 9 | 21,000,000 |
| 12 | São Paulo | 1 | 20,935,204 |
| 13 | Mumbai | 25 | 20,748,395 |
| 14 | New York City | 20 | 20,092,883 |
| 15 | Osaka | 28 | 19,342,000 |
| 16 | Wuhan | 13 | 19,000,000 |
| 17 | Chengdu | 13 | 18,100,000 |
| 18 | Dhaka | 4 | 17,151,925 |
| 19 | Chongqing | 13 | 17,000,000 |
| 20 | Tianjin | 13 | 15,400,000 |
| 21 | Kolkata | 25 | 14,617,882 |
| 22 | Tehran | 11 | 14,595,904 |
| 23 | Istanbul | 2 | 14,377,018 |
| 24 | London | 26 | 14,031,830 |
| 25 | Hangzhou | 13 | 13,400,000 |
| 26 | Los Angeles | 20 | 13,262,220 |
| 27 | Buenos Aires | 8 | 13,074,000 |
| 28 | Xi'an | 13 | 12,900,000 |
| 29 | Paris | 6 | 12,405,426 |
| 30 | Changzhou | 13 | 12,400,000 |
| 31 | Shantou | 13 | 12,000,000 |
| 32 | Rio de Janeiro | 1 | 11,973,505 |
| 33 | Manila | 18 | 11,855,975 |
| 34 | Nanjing | 13 | 11,700,000 |
| 35 | Rhine-Ruhr | 16 | 11,470,000 |
| 36 | Jinan | 13 | 11,000,000 |
| 37 | Bangalore | 25 | 10,576,167 |
| 38 | Harbin | 13 | 10,500,000 |
| 39 | Lima | 7 | 9,886,647 |
| 40 | Zhengzhou | 13 | 9,700,000 |
| 41 | Qingdao | 13 | 9,600,000 |
| 42 | Chicago | 20 | 9,554,598 |
| 43 | Nagoya | 28 | 9,107,000 |
| 44 | Chennai | 25 | 8,917,749 |
| 45 | Bangkok | 15 | 8,305,218 |
| 46 | Bogotá | 27 | 7,878,783 |
| 47 | Hyderabad | 25 | 7,749,334 |
| 48 | Shenyang | 13 | 7,700,000 |
| 49 | Wenzhou | 13 | 7,600,000 |
| 50 | Nanchang | 13 | 7,400,000 |
| 51 | Hong Kong | 13 | 7,298,600 |
| 52 | Taipei | 29 | 7,045,488 |
| 53 | Dallas–Fort Worth | 20 | 6,954,330 |
| 54 | Santiago | 14 | 6,683,852 |
| 55 | Luanda | 23 | 6,542,944 |
| 56 | Houston | 20 | 6,490,180 |
| 57 | Madrid | 17 | 6,378,297 |
| 58 | Ahmedabad | 25 | 6,352,254 |
| 59 | Toronto | 5 | 6,055,724 |
| 60 | Philadelphia | 20 | 6,051,170 |
| 61 | Washington, D.C. | 20 | 6,033,737 |
| 62 | Miami | 20 | 5,929,819 |
| 63 | Belo Horizonte | 1 | 5,767,414 |
| 64 | Atlanta | 20 | 5,614,323 |
| 65 | Singapore | 12 | 5,535,000 |
| 66 | Barcelona | 17 | 5,445,616 |
| 67 | Munich | 16 | 5,203,738 |
| 68 | Stuttgart | 16 | 5,200,000 |
| 69 | Ankara | 2 | 5,150,072 |
| 70 | Hamburg | 16 | 5,100,000 |
| 71 | Pune | 25 | 5,049,968 |
| 72 | Berlin | 16 | 5,005,216 |
| 73 | Guadalajara | 24 | 4,796,050 |
| 74 | Boston | 20 | 4,732,161 |
| 75 | Sydney | 10 | 5,000,500 |
| 76 | San Francisco | 20 | 4,594,060 |
| 77 | Surat | 25 | 4,585,367 |
| 78 | Phoenix | 20 | 4,489,109 |
| 79 | Monterrey | 24 | 4,477,614 |
| 80 | Inland Empire | 20 | 4,441,890 |
| 81 | Rome | 3 | 4,321,244 |
| 82 | Detroit | 20 | 4,296,611 |
| 83 | Milan | 3 | 4,267,946 |
| 84 | Melbourne | 10 | 4,650,000 |
| countries | |
|---|---|
| id | name |
| 1 | Brazil |
| 2 | Turkey |
| 3 | Italy |
| 4 | Bangladesh |
| 5 | Canada |
| 6 | France |
| 7 | Peru |
| 8 | Argentina |
| 9 | Nigeria |
| 10 | Australia |
| 11 | Iran |
| 12 | Singapore |
| 13 | China |
| 14 | Chile |
| 15 | Thailand |
| 16 | Germany |
| 17 | Spain |
| 18 | Philippines |
| 19 | Indonesia |
| 20 | United States |
| 21 | South Korea |
| 22 | Pakistan |
| 23 | Angola |
| 24 | Mexico |
| 25 | India |
| 26 | United Kingdom |
| 27 | Colombia |
| 28 | Japan |
| 29 | Taiwan |
For the first example we are only going to retrieve the top 5 most populous cities and render it as a pie chart. In this strategy we are going to return the chart data as part of the view context and inject the results in the JavaScript code using the Django Template language.
views.py
from django.shortcuts import render
from mysite.core.models import City
def pie_chart(request):
labels = []
data = []
queryset = City.objects.order_by('-population')[:5]
for city in queryset:
labels.append(city.name)
data.append(city.population)
return render(request, 'pie_chart.html', {
'labels': labels,
'data': data,
})Basically in the view above we are iterating through the City queryset and building a list of labels and a list of
data. Here in this case the data is the population count saved in the City model.
For the urls.py just a simple routing:
urls.py
from django.urls import path
from mysite.core import views
urlpatterns = [
path('pie-chart/', views.pie_chart, name='pie-chart'),
]Now the template. I got a basic snippet from the Chart.js Pie Chart Documentation.
pie_chart.html
{% extends 'base.html' %}
{% block content %}
<div id="container" style="width: 75%;">
<canvas id="pie-chart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
<script>
var config = {
type: 'pie',
data: {
datasets: [{
data: {{ data|safe }},
backgroundColor: [
'#696969', '#808080', '#A9A9A9', '#C0C0C0', '#D3D3D3'
],
label: 'Population'
}],
labels: {{ labels|safe }}
},
options: {
responsive: true
}
};
window.onload = function() {
var ctx = document.getElementById('pie-chart').getContext('2d');
window.myPie = new Chart(ctx, config);
};
</script>
{% endblock %}In the example above the base.html template is not important but you can see it in the code example I shared in the
end of this post.
This strategy is not ideal but works fine. The bad thing is that we are using the Django Template Language to interfere
with the JavaScript logic. When we put {{ data|safe}} we are injecting a variable that came from
the server directly in the JavaScript code.
The code above looks like this:
![]()
As the title says, we are now going to render a bar chart using an async call.
views.py
from django.shortcuts import render
from django.db.models import Sum
from django.http import JsonResponse
from mysite.core.models import City
def home(request):
return render(request, 'home.html')
def population_chart(request):
labels = []
data = []
queryset = City.objects.values('country__name').annotate(country_population=Sum('population')).order_by('-country_population')
for entry in queryset:
labels.append(entry['country__name'])
data.append(entry['country_population'])
return JsonResponse(data={
'labels': labels,
'data': data,
})So here we are using two views. The home view would be the main page where the chart would be loaded at. The other
view population_chart would be the one with the sole responsibility to aggregate the data the return a JSON response
with the labels and data.
If you are wondering about what this queryset is doing, it is grouping the cities by the country and aggregating the total population of each country. The result is going to be a list of country + total population. To learn more about this kind of query have a look on this post: How to Create Group By Queries With Django ORM
urls.py
from django.urls import path
from mysite.core import views
urlpatterns = [
path('', views.home, name='home'),
path('population-chart/', views.population_chart, name='population-chart'),
]home.html
{% extends 'base.html' %}
{% block content %}
<div id="container" style="width: 75%;">
<canvas id="population-chart" data-url="{% url 'population-chart' %}"></canvas>
</div>
<script src="https://code.jquery.com/jquery-3.4.1.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/chart.js@2.9.3/dist/Chart.min.js"></script>
<script>
$(function () {
var $populationChart = $("#population-chart");
$.ajax({
url: $populationChart.data("url"),
success: function (data) {
var ctx = $populationChart[0].getContext("2d");
new Chart(ctx, {
type: 'bar',
data: {
labels: data.labels,
datasets: [{
label: 'Population',
backgroundColor: 'blue',
data: data.data
}]
},
options: {
responsive: true,
legend: {
position: 'top',
},
title: {
display: true,
text: 'Population Bar Chart'
}
}
});
}
});
});
</script>
{% endblock %}Now we have a better separation of concerns. Looking at the chart container:
<canvas id="population-chart" data-url="{% url 'population-chart' %}"></canvas>We added a reference to the URL that holds the chart rendering logic. Later on we are using it to execute the Ajax call.
var $populationChart = $("#population-chart");
$.ajax({
url: $populationChart.data("url"),
success: function (data) {
// ...
}
});Inside the success callback we then finally execute the Chart.js related code using the JsonResponse data.
![]()
I hope this tutorial helped you to get started with working with charts using Chart.js. I published another tutorial on the same subject a while ago but using the Highcharts library. The approach is pretty much the same: How to Integrate Highcharts.js with Django.
If you want to grab the code I used in this tutorial you can find it here: github.com/sibtc/django-chartjs-example.
In the latest Explaining Computers video, Christopher Barnatt explains how to use servo motors with Raspberry Pi. Using servos is a great introduction to the digital making side of computing; servos allow you to control the movement of all manner of project components with your Raspberry Pi and a motor controller attached to its GPIO pins.
Raspberry Pi Servo Motor Control
Control of SG90 servos in Python on a Raspberry Pi, including an explanation of PWM and how a servo differs from a motor. You can download the code from the video at: https://www.explainingcomputers.com/pi_servos_video.html The five-pack of SG90 servos used in this video was purchased on Amazon.co.uk here: https://www.amazon.co.uk/dp/B07H9VC698/ref=nosim?tag=explainin-21 with a similar product on Amazon.com here: https://amzn.to/2QHshx3 (affiliate links).
Christopher picked up his SG90 servo motors online, where you’ll find a variety of servo options. What type of servo you need depends on the project you want to create, so be sure to consider the weight and size of what you plan to move, and the speed at which you need to move it.
As the motor controller connects via GPIO, you can even use the tiny £5 Raspberry Pi Zero to control your servo, which makes adding movement to your projects an option even when you’re under tight space constraints.
For other detailed computing videos, be sure to subscribe to the Explaining Computers YouTube channel.
And for more Raspberry Pi projects, check out the Raspberry Pi projects page.
We’re always looking for people to join our incredible community of translators to help us translate our free resources, including the free projects found on our projects page.
If you speak English and another language and would like to give a portion of your time to making our resources available to more people across the globe, sign up as a translator today.
The post How to control multiple servo motors with Raspberry Pi appeared first on Raspberry Pi.
If you own a 3D printer, you’ll likely have at least heard of OctoPrint, created by Gina Häußge and maintained by her and Guy Sheffer! OctoPrint has the potential to transform your 3D printing workflow for the better, and it’s very easy to set up. This guide will take you through the setup process step by step, and give you some handy tips along the way.
Before we start finding out how to install OctoPrint, let’s look at why you might want to. OctoPrint is a piece of open-source software that allows us to add WiFi functionality to any 3D printer with a USB port (which is pretty much all of them). More specifically, you’ll be able to drop files from your computer onto your printer, start/stop prints, monitor your printer via a live video feed, control the motors, control the temperature, and more, all from your web browser. Of course, with great power comes great responsibility — 3D printers have parts that are hot enough to cause fires, so make sure you have a safe setup, which may include not letting it run unsupervised.
• Raspberry Pi 3 (or newer)
• MicroSD card
• Raspberry Pi power adapter
• USB cable (the connector type will depend on your printer)
• Webcam/Raspberry Pi Camera Module (optional)
• 3D-printed camera mount (optional)
Before we get started, it is not recommended that anything less than a Raspberry Pi 3 is used for this project. There have been reports of limited success using OctoPrint on a Raspberry Pi Zero W, but only if you have no intention of using a camera to monitor your prints. If you want to try this with a Pi Zero or an older Raspberry Pi, you may experience unexpected print failures.
Firstly, you will need to download the latest version of OctoPi from the OctoPrint website. OctoPi (created by Guy Sheffer) is a Raspbian distribution that comes with OctoPrint, video streaming software, and CuraEngine for slicing models on your Raspberry Pi. When this has finished downloading, unzip the file and put the resulting IMG file somewhere handy.
Next, we need to flash this image onto our microSD card. We recommend using Etcher to do this, due to its minimal UI and ease of use; plus it’s also available to use on both Windows and Mac. Get it here: balena.io/etcher. When Etcher is installed and running, you’ll see the UI displayed. Simply click the Select Image button and find the IMG file you unzipped earlier. Next, put your microSD card into your computer and select it in the middle column of the Etcher interface.
Finally, click on Flash!, and while the image is being burned onto the card, get your WiFi router details, as you’ll need them for the next step.
Now that you have your operating system, you’ll want to add your WiFi details so that the Raspberry Pi can automatically connect to your network after it’s booted. To do this, remove the microSD card from your computer (Etcher will have ‘ejected’ the card after it has finished burning the image onto it) and then plug it back in again. Navigate to the microSD card on your computer — it should now be called boot — and open the file called octopi-wpa-supplicant.txt. Editing this file using WordPad or TextEdit can cause formatting issues; we recommend using Notepad++ to update this file, but there are instructions within the file itself to mitigate formatting issues if you do choose to use another text editor. Find the section that begins ## WPA/WPA2 secured and remove the hash signs from the four lines below this one to uncomment them. Finally, replace the SSID value and the PSK value with the name and password for your WiFi network, respectively (keeping the quotation marks). See the example below for how this should look.
Further down in the file, there is a section for what country you are in. If you are using OctoPrint in the UK, leave this as is (by default, the UK is selected). However, if you wish to change this, simply comment the UK line again by adding a # before it, and uncomment whichever country you are setting up OctoPrint in. The example below shows how the file will look if you are setting this up for use in the US:
# Uncomment the country your Pi is in to activate Wifi in RaspberryPi 3 B+ and above # For full list see: https://en.wikipedia.org/ wiki/ISO_3166-1_alpha-2 #country=GB # United Kingdom #country=CA # Canada #country=DE # Germany #country=FR # France country=US # United States
When the changes have been made, save the file and then eject/unmount and remove the microSD card from your computer and put it into your Raspberry Pi. Plug the power supply in, and go and make a cup of tea while it boots up for the first time (this may take around ten minutes). Make sure the Raspberry Pi is running as expected (i.e. check that the green status LED is flashing intermittently). If you’re using macOS, visit octopi.local in your browser of choice. If you’re using Windows, you can find OctoPrint by clicking on the Network tab in the sidebar. It should be called OctoPrint instance on octopi – double-clicking on this will open the OctoPrint dashboard in your browser.
If you see the screen shown above, then congratulations! You have set up OctoPrint.
Not seeing that OctoPrint splash screen? Fear not, you are not the first. While a full list of issues is beyond the scope of this article, common issues include: double-checking your WiFi details are entered correctly in the octopi-wpa-supplicant.txt file, ensuring your Raspberry Pi is working correctly (plug the Raspberry Pi into a monitor and watch what happens during boot), or your Raspberry Pi may be out of range of your WiFi router. There’s a detailed list of troubleshooting suggestions on the OctoPrint website.
We now have the opportunity to set up OctoPrint for our printer using the handy wizard. Most of this is very straightforward — setting up a password, signing up to send anonymous usage stats, etc. — but there are a few sections which require a little more thought.
We recommend enabling the connectivity check and the plug-ins blacklist to help keep things nice and stable. If you plan on using OctoPrint as your slicer as well as a monitoring tool, then you can use this step to import a Cura profile. However, we recommend skipping this step as it’s much quicker (and you can use a slicer of your choice) to slice the model on your computer, and then send the finished G-code over.
Finally, we need to put in our printer details. Above, we’ve included some of the specs of the Creality Ender-3 as an example. If you can’t find the exact details of your printer, a quick web search should show what you need for this section.
The General tab can have anything in it, it’s just an identifier for your own use. Print bed & build volume should be easy to find out — if not, you can measure your print bed and find out the position of the origin by looking at your Cura printer profile. Leave Axes as default; for the Hotend and extruder section, defaults are almost certainly fine here (unless you’ve changed your nozzle; 0.4 is the default diameter for most consumer printers).
Now that you’re set up with OctoPrint, you’re ready to start printing. Turn off your Raspberry Pi, then plug it into your 3D printer. After it has booted up, open OctoPrint again in your browser and take your newly WiFi-enabled printer for a spin by clicking the Connect button. After it has connected, you’ll be able to set the hot end and bed temperature, then watch as the real-time readings are updated.
In the Control tab, we can see the camera stream (if you’re using one) and the motor controls, as well as commands to home the axes. There’s a G-code file viewer to look through a cross-section of the currently loaded model, and a terminal to send custom G-code commands to your printer. The last tab is for making time-lapses; however, there is a plug-in available to help with this process.
Undoubtedly the easiest way to set up video monitoring of your prints is to use the official Raspberry Pi Camera Module. There are dozens of awesome mounts on Thingiverse for a Raspberry Pi Camera Module, to allow you to get the best angle of your models as they print. There are also some awesome OctoPrint-themed Raspberry Pi cases to house your new printer brains. While it isn’t officially supported by OctoPrint, you can use a USB webcam instead if you have one handy, or just want some very high-quality video streams. The OctoPrint wiki has a crowdsourced list of webcams known to work, as well as a link for the extra steps needed to get the webcam working correctly.
As mentioned earlier, our recommended way of printing a model using OctoPrint is to first use your slicer as you would if you were creating a file to save to a microSD card. Once you have the file, save it somewhere handy on your computer, and open the OctoPrint interface. In the bottom left of the screen, you will see the Upload File button — click this and upload the G-code you wish to print.
You’ll see the file/print details appear, including information on how long it’ll take for the object to print. Before you kick things off, check out the G-code Viewer tab on the right. You can not only scroll through the layers of the object, but, using the slider at the bottom, you can see the exact pattern the 3D printer will use to ‘draw’ each layer. Now click Print and watch your printer jump into action!
OctoPrint has scores of community-created plug-ins, but our favourite, Octolapse, makes beautiful hypnotic time-lapses. What makes them so special is that the plug-in alters the G-code of whatever object you are printing so that once each layer has finished, the extruder moves away from the print to let the camera take an unobstructed shot of the model. The result is an object that seems to grow out of the build plate as if by magic. You’ll not find a finer example of it than here.
Satisfying 3D Prints TimeLapse episode 7 (Prusa I3 Mk3 octopi)
3D Printing timelapses of models printed on the Prusa i3 MK3! Here’s another compilation of my recent timelapses. I got some shots that i think came out really great and i hope you enjoy them! as always if you want to see some of these timelapses before they come out or want to catch some behind the scenes action check out my instagram!
OctoPrint was created and is maintained by Gina Häußge (@foosel) and Guy Sheffer (@GuySoft)! A big thank you to both of them for putting in many, many volunteer hours to provide the world with an amazing piece of free, open-source software.
Head over to the OctoPrint website to find out how you can support OctoPrint’s continued existence.
This tutorial comes fresh from the pages of HackSpace magazine issue 26 and was written by Glenn Horan. Thanks, Glenn.
To get your copy of HackSpace magazine issue 26, visit your local newsagent, the Raspberry Pi Store, Cambridge, or the Raspberry Pi Press online store.
Fans of HackSpace magazine will also score themselves a rather delightful Adafruit Circuit Playground Express with a 12-month subscription. Sweet!
The post How to set up OctoPrint on your Raspberry Pi appeared first on Raspberry Pi.
In this tutorial you are going to learn how to pass extra data to your serializer, before saving it to the database.
When using regular Django forms, there is this common pattern where we save the form with commit=False and then pass
some extra data to the instance before saving it to the database, like this:
form = InvoiceForm(request.POST)
if form.is_valid():
invoice = form.save(commit=False)
invoice.user = request.user
invoice.save()This is very useful because we can save the required information using only one database query and it also make it possible to handle not nullable columns that was not defined in the form.
To simulate this pattern using a Django REST Framework serializer you can do something like this:
serializer = InvoiceSerializer(data=request.data)
if serializer.is_valid():
serializer.save(user=request.user)You can also pass several parameters at once:
serializer = InvoiceSerializer(data=request.data)
if serializer.is_valid():
serializer.save(user=request.user, date=timezone.now(), status='sent')In this example I created an app named core.
models.py
from django.contrib.auth.models import User
from django.db import models
class Invoice(models.Model):
SENT = 1
PAID = 2
VOID = 3
STATUS_CHOICES = (
(SENT, 'sent'),
(PAID, 'paid'),
(VOID, 'void'),
)
user = models.ForeignKey(User, on_delete=models.CASCADE, related_name='invoices')
number = models.CharField(max_length=30)
date = models.DateTimeField(auto_now_add=True)
status = models.PositiveSmallIntegerField(choices=STATUS_CHOICES)
amount = models.DecimalField(max_digits=10, decimal_places=2)serializers.py
from rest_framework import serializers
from core.models import Invoice
class InvoiceSerializer(serializers.ModelSerializer):
class Meta:
model = Invoice
fields = ('number', 'amount')views.py
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from core.models import Invoice
from core.serializers import InvoiceSerializer
class InvoiceAPIView(APIView):
def post(self, request):
serializer = InvoiceSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
serializer.save(user=request.user, status=Invoice.SENT)
return Response(status=status.HTTP_201_CREATED)Very similar example, using the same models.py and serializers.py as in the previous example.
views.py
from rest_framework.viewsets import ModelViewSet
from core.models import Invoice
from core.serializers import InvoiceSerializer
class InvoiceViewSet(ModelViewSet):
queryset = Invoice.objects.all()
serializer_class = InvoiceSerializer
def perform_create(self, serializer):
serializer.save(user=self.request.user, status=Invoice.SENT)In this tutorial we are going to explore three date/datetime pickers options that you can easily use in a Django project. We are going to explore how to do it manually first, then how to set up a custom widget and finally how to use a third-party Django app with support to datetime pickers.
The implementation of a date picker is mostly done on the front-end.
The key part of the implementation is to assure Django will receive the date input value in the correct format, and also that Django will be able to reproduce the format when rendering a form with initial data.
We can also use custom widgets to provide a deeper integration between the front-end and back-end and also to promote better reuse throughout a project.
In the next sections we are going to explore following date pickers:
Tempus Dominus Bootstrap 4 Docs Source
![]()
XDSoft DateTimePicker Docs Source
![]()
Fengyuan Chen’s Datepicker Docs Source
![]()
This is a great JavaScript library and it integrate well with Bootstrap 4. The downside is that it requires moment.js and sort of need Font-Awesome for the icons.
It only make sense to use this library with you are already using Bootstrap 4 + jQuery, otherwise the list of CSS and JS may look a little bit overwhelming.
To install it you can use their CDN or download the latest release from their GitHub Releases page.
If you downloaded the code from the releases page, grab the processed code from the build/ folder.
Below, a static HTML example of the datepicker:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Static Example</title>
<!-- Bootstrap 4 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossorigin="anonymous">
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.6/umd/popper.min.js" integrity="sha384-wHAiFfRlMFy6i5SRaxvfOCifBUQy1xHdJ/yoi7FRNXMRBu5WHdZYu1hA6ZOblgut" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/js/bootstrap.min.js" integrity="sha384-B0UglyR+jN6CkvvICOB2joaf5I4l3gm9GU6Hc1og6Ls7i6U/mkkaduKaBhlAXv9k" crossorigin="anonymous"></script>
<!-- Font Awesome -->
<link href="https://stackpath.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous">
<!-- Moment.js -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.23.0/moment.min.js" integrity="sha256-VBLiveTKyUZMEzJd6z2mhfxIqz3ZATCuVMawPZGzIfA=" crossorigin="anonymous"></script>
<!-- Tempus Dominus Bootstrap 4 -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/css/tempusdominus-bootstrap-4.min.css" integrity="sha256-XPTBwC3SBoWHSmKasAk01c08M6sIA5gF5+sRxqak2Qs=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/tempusdominus-bootstrap-4/5.1.2/js/tempusdominus-bootstrap-4.min.js" integrity="sha256-z0oKYg6xiLq3yJGsp/LsY9XykbweQlHl42jHv2XTBz4=" crossorigin="anonymous"></script>
</head>
<body>
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
<input type="text" class="form-control datetimepicker-input" data-target="#datetimepicker1"/>
<div class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
<script>
$(function () {
$("#datetimepicker1").datetimepicker();
});
</script>
</body>
</html>The challenge now is to have this input snippet integrated with a Django form.
forms.py
from django import forms
class DateForm(forms.Form):
date = forms.DateTimeField(
input_formats=['%d/%m/%Y %H:%M'],
widget=forms.DateTimeInput(attrs={
'class': 'form-control datetimepicker-input',
'data-target': '#datetimepicker1'
})
)template
<div class="input-group date" id="datetimepicker1" data-target-input="nearest">
{{ form.date }}
<div class="input-group-append" data-target="#datetimepicker1" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
<script>
$(function () {
$("#datetimepicker1").datetimepicker({
format: 'DD/MM/YYYY HH:mm',
});
});
</script>The script tag can be placed anywhere because the snippet $(function () { ... }); will run the datetimepicker
initialization when the page is ready. The only requirement is that this script tag is placed after the jQuery script
tag.
You can create the widget in any app you want, here I’m going to consider we have a Django app named core.
core/widgets.py
from django.forms import DateTimeInput
class BootstrapDateTimePickerInput(DateTimeInput):
template_name = 'widgets/bootstrap_datetimepicker.html'
def get_context(self, name, value, attrs):
datetimepicker_id = 'datetimepicker_{name}'.format(name=name)
if attrs is None:
attrs = dict()
attrs['data-target'] = '#{id}'.format(id=datetimepicker_id)
attrs['class'] = 'form-control datetimepicker-input'
context = super().get_context(name, value, attrs)
context['widget']['datetimepicker_id'] = datetimepicker_id
return contextIn the implementation above we generate a unique ID datetimepicker_id and also include it in the widget context.
Then the front-end implementation is done inside the widget HTML snippet.
widgets/bootstrap_datetimepicker.html
<div class="input-group date" id="{{ widget.datetimepicker_id }}" data-target-input="nearest">
{% include "django/forms/widgets/input.html" %}
<div class="input-group-append" data-target="#{{ widget.datetimepicker_id }}" data-toggle="datetimepicker">
<div class="input-group-text"><i class="fa fa-calendar"></i></div>
</div>
</div>
<script>
$(function () {
$("#{{ widget.datetimepicker_id }}").datetimepicker({
format: 'DD/MM/YYYY HH:mm',
});
});
</script>Note how we make use of the built-in django/forms/widgets/input.html template.
Now the usage:
core/forms.py
from .widgets import BootstrapDateTimePickerInput
class DateForm(forms.Form):
date = forms.DateTimeField(
input_formats=['%d/%m/%Y %H:%M'],
widget=BootstrapDateTimePickerInput()
)Now simply render the field:
template
{{ form.date }}The good thing about having the widget is that your form could have several date fields using the widget and you could simply render the whole form like:
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<input type="submit" value="Submit">
</form>The XDSoft DateTimePicker is a very versatile date picker and doesn’t rely on moment.js or Bootstrap, although it looks good in a Bootstrap website.
It is easy to use and it is very straightforward.
You can download the source from GitHub releases page.
Below, a static example so you can see the minimum requirements and how all the pieces come together:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Static Example</title>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<!-- XDSoft DateTimePicker -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.min.css" integrity="sha256-DOS9W6NR+NFe1fUhEE0PGKY/fubbUCnOfTje2JMDw3Y=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js" integrity="sha256-FEqEelWI3WouFOo2VWP/uJfs1y8KJ++FLh2Lbqc8SJk=" crossorigin="anonymous"></script>
</head>
<body>
<input id="datetimepicker" type="text">
<script>
$(function () {
$("#datetimepicker").datetimepicker();
});
</script>
</body>
</html>A basic integration with Django would look like this:
forms.py
from django import forms
class DateForm(forms.Form):
date = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'])Simple form, default widget, nothing special.
Now using it on the template:
template
{{ form.date }}
<script>
$(function () {
$("#id_date").datetimepicker({
format: 'd/m/Y H:i',
});
});
</script>The id_date is the default ID Django generates for the form fields (id_ + name).
core/widgets.py
from django.forms import DateTimeInput
class XDSoftDateTimePickerInput(DateTimeInput):
template_name = 'widgets/xdsoft_datetimepicker.html'widgets/xdsoft_datetimepicker.html
{% include "django/forms/widgets/input.html" %}
<script>
$(function () {
$("input[name='{{ widget.name }}']").datetimepicker({
format: 'd/m/Y H:i',
});
});
</script>To have a more generic implementation, this time we are selecting the field to initialize the component using its name instead of its id, should the user change the id prefix.
Now the usage:
core/forms.py
from django import forms
from .widgets import XDSoftDateTimePickerInput
class DateForm(forms.Form):
date = forms.DateTimeField(
input_formats=['%d/%m/%Y %H:%M'],
widget=XDSoftDateTimePickerInput()
)template
{{ form.date }}This is a very beautiful and minimalist date picker. Unfortunately there is no time support. But if you only need dates this is a great choice.
To install this datepicker you can either use their CDN or download the sources from their GitHub releases page. Please note that they do not provide a compiled/processed JavaScript files. But you can download those to your local machine using the CDN.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<title>Static Example</title>
<style>body {font-family: Arial, sans-serif;}</style>
<!-- jQuery -->
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></script>
<!-- Fengyuan Chen's Datepicker -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/datepicker/0.6.5/datepicker.min.css" integrity="sha256-b88RdwbRJEzRx95nCuuva+hO5ExvXXnpX+78h8DjyOE=" crossorigin="anonymous" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/datepicker/0.6.5/datepicker.min.js" integrity="sha256-/7FLTdzP6CfC1VBAj/rsp3Rinuuu9leMRGd354hvk0k=" crossorigin="anonymous"></script>
</head>
<body>
<input id="datepicker">
<script>
$(function () {
$("#datepicker").datepicker();
});
</script>
</body>
</html>A basic integration with Django (note that we are now using DateField instead of DateTimeField):
forms.py
from django import forms
class DateForm(forms.Form):
date = forms.DateTimeField(input_formats=['%d/%m/%Y %H:%M'])template
{{ form.date }}
<script>
$(function () {
$("#id_date").datepicker({
format:'dd/mm/yyyy',
});
});
</script>core/widgets.py
from django.forms import DateInput
class FengyuanChenDatePickerInput(DateInput):
template_name = 'widgets/fengyuanchen_datepicker.html'widgets/fengyuanchen_datepicker.html
{% include "django/forms/widgets/input.html" %}
<script>
$(function () {
$("input[name='{{ widget.name }}']").datepicker({
format:'dd/mm/yyyy',
});
});
</script>Usage:
core/forms.py
from django import forms
from .widgets import FengyuanChenDatePickerInput
class DateForm(forms.Form):
date = forms.DateTimeField(
input_formats=['%d/%m/%Y %H:%M'],
widget=FengyuanChenDatePickerInput()
)template
{{ form.date }}The implementation is very similar no matter what date/datetime picker you are using. Hopefully this tutorial provided some insights on how to integrate this kind of frontend library to a Django project.
As always, the best source of information about each of those libraries are their official documentation.
I also created an example project to show the usage and implementation of the widgets for each of the libraries presented in this tutorial. Grab the source code at github.com/sibtc/django-datetimepicker-example.
The Django forms API have two field types to work with multiple options: ChoiceField and ModelChoiceField.
Both use select input as the default widget and they work in a similar way, except that ModelChoiceField is designed
to handle QuerySets and work with foreign key relationships.
A basic implementation using a ChoiceField would be:
class ExpenseForm(forms.Form):
CHOICES = (
(11, 'Credit Card'),
(12, 'Student Loans'),
(13, 'Taxes'),
(21, 'Books'),
(22, 'Games'),
(31, 'Groceries'),
(32, 'Restaurants'),
)
amount = forms.DecimalField()
date = forms.DateField()
category = forms.ChoiceField(choices=CHOICES)You can also organize the choices in groups to generate the <optgroup> tags like this:
class ExpenseForm(forms.Form):
CHOICES = (
('Debt', (
(11, 'Credit Card'),
(12, 'Student Loans'),
(13, 'Taxes'),
)),
('Entertainment', (
(21, 'Books'),
(22, 'Games'),
)),
('Everyday', (
(31, 'Groceries'),
(32, 'Restaurants'),
)),
)
amount = forms.DecimalField()
date = forms.DateField()
category = forms.ChoiceField(choices=CHOICES)When you are using a ModelChoiceField unfortunately there is no built-in solution.
Recently I found a nice solution on Django’s ticket tracker, where
someone proposed adding an opt_group argument to the ModelChoiceField.
While the discussion is still ongoing, Simon Charette proposed a really good solution.
Let’s see how we can integrate it in our project.
First consider the following models:
models.py
from django.db import models
class Category(models.Model):
name = models.CharField(max_length=30)
parent = models.ForeignKey('Category', on_delete=models.CASCADE, null=True)
def __str__(self):
return self.name
class Expense(models.Model):
amount = models.DecimalField(max_digits=10, decimal_places=2)
date = models.DateField()
category = models.ForeignKey(Category, on_delete=models.CASCADE)
def __str__(self):
return self.amountSo now our category instead of being a regular choices field it is now a model and the Expense model have a
relationship with it using a foreign key.
If we create a ModelForm using this model, the result will be very similar to our first example.
To simulate a grouped categories you will need the code below. First create a new module named fields.py:
fields.py
from functools import partial
from itertools import groupby
from operator import attrgetter
from django.forms.models import ModelChoiceIterator, ModelChoiceField
class GroupedModelChoiceIterator(ModelChoiceIterator):
def __init__(self, field, groupby):
self.groupby = groupby
super().__init__(field)
def __iter__(self):
if self.field.empty_label is not None:
yield ("", self.field.empty_label)
queryset = self.queryset
# Can't use iterator() when queryset uses prefetch_related()
if not queryset._prefetch_related_lookups:
queryset = queryset.iterator()
for group, objs in groupby(queryset, self.groupby):
yield (group, [self.choice(obj) for obj in objs])
class GroupedModelChoiceField(ModelChoiceField):
def __init__(self, *args, choices_groupby, **kwargs):
if isinstance(choices_groupby, str):
choices_groupby = attrgetter(choices_groupby)
elif not callable(choices_groupby):
raise TypeError('choices_groupby must either be a str or a callable accepting a single argument')
self.iterator = partial(GroupedModelChoiceIterator, groupby=choices_groupby)
super().__init__(*args, **kwargs)And here is how you use it in your forms:
forms.py
from django import forms
from .fields import GroupedModelChoiceField
from .models import Category, Expense
class ExpenseForm(forms.ModelForm):
category = GroupedModelChoiceField(
queryset=Category.objects.exclude(parent=None),
choices_groupby='parent'
)
class Meta:
model = Expense
fields = ('amount', 'date', 'category')Because in the example above I used a self-referencing relationship I had to add the exclude(parent=None) to hide
the “group categories” from showing up in the select input as a valid option.
You can download the code used in this tutorial from GitHub: github.com/sibtc/django-grouped-choice-field-example
Credits to the solution Simon Charette on Django Ticket Track.
JWT stand for JSON Web Token and it is an authentication strategy used by client/server applications where the client is a Web application using JavaScript and some frontend framework like Angular, React or VueJS.
In this tutorial we are going to explore the specifics of JWT authentication. If you want to learn more about Token-based authentication using Django REST Framework (DRF), or if you want to know how to start a new DRF project you can read this tutorial: How to Implement Token Authentication using Django REST Framework. The concepts are the same, we are just going to switch the authentication backend.
The JWT is just an authorization token that should be included in all requests:
curl http://127.0.0.1:8000/hello/ -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQzODI4NDMxLCJqdGkiOiI3ZjU5OTdiNzE1MGQ0NjU3OWRjMmI0OTE2NzA5N2U3YiIsInVzZXJfaWQiOjF9.Ju70kdcaHKn1Qaz8H42zrOYk0Jx9kIckTn9Xx7vhikY'The JWT is acquired by exchanging an username + password for an access token and an refresh token.
The access token is usually short-lived (expires in 5 min or so, can be customized though).
The refresh token lives a little bit longer (expires in 24 hours, also customizable). It is comparable to an authentication session. After it expires, you need a full login with username + password again.
Why is that?
It’s a security feature and also it’s because the JWT holds a little bit more information. If you look closely the example I gave above, you will see the token is composed by three parts:
xxxxx.yyyyy.zzzzzThose are three distinctive parts that compose a JWT:
header.payload.signatureSo we have here:
header = eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
payload = eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQzODI4NDMxLCJqdGkiOiI3ZjU5OTdiNzE1MGQ0NjU3OWRjMmI0OTE2NzA5N2U3YiIsInVzZXJfaWQiOjF9
signature = Ju70kdcaHKn1Qaz8H42zrOYk0Jx9kIckTn9Xx7vhikYThis information is encoded using Base64. If we decode, we will see something like this:
header
{
"typ": "JWT",
"alg": "HS256"
}payload
{
"token_type": "access",
"exp": 1543828431,
"jti": "7f5997b7150d46579dc2b49167097e7b",
"user_id": 1
}signature
The signature is issued by the JWT backend, using the header base64 + payload base64 + SECRET_KEY. Upon each request
this signature is verified. If any information in the header or in the payload was changed by the client it will
invalidate the signature. The only way of checking and validating the signature is by using your application’s
SECRET_KEY. Among other things, that’s why you should always keep your SECRET_KEY secret!
For this tutorial we are going to use the djangorestframework_simplejwt
library, recommended by the DRF developers.
pip install djangorestframework_simplejwtsettings.py
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework_simplejwt.authentication.JWTAuthentication',
],
}urls.py
from django.urls import path
from rest_framework_simplejwt import views as jwt_views
urlpatterns = [
# Your URLs...
path('api/token/', jwt_views.TokenObtainPairView.as_view(), name='token_obtain_pair'),
path('api/token/refresh/', jwt_views.TokenRefreshView.as_view(), name='token_refresh'),
]For this tutorial I will use the following route and API view:
views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated
class HelloView(APIView):
permission_classes = (IsAuthenticated,)
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)urls.py
from django.urls import path
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
]I will be using HTTPie to consume the API endpoints via the terminal. But you can also use cURL (readily available in many OS) to try things out locally.
Or alternatively, use the DRF web interface by accessing the endpoint URLs like this:
![]()
First step is to authenticate and obtain the token. The endpoint is /api/token/ and it only accepts POST requests.
http post http://127.0.0.1:8000/api/token/ username=vitor password=123![]()
So basically your response body is the two tokens:
{
"access": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjU5LCJqdGkiOiIyYmQ1NjI3MmIzYjI0YjNmOGI1MjJlNThjMzdjMTdlMSIsInVzZXJfaWQiOjF9.D92tTuVi_YcNkJtiLGHtcn6tBcxLCBxz9FKD3qzhUg8",
"refresh": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0NTMxMDM1OSwianRpIjoiMjk2ZDc1ZDA3Nzc2NDE0ZjkxYjhiOTY4MzI4NGRmOTUiLCJ1c2VyX2lkIjoxfQ.rA-mnGRg71NEW_ga0sJoaMODS5ABjE5HnxJDb0F8xAo"
}After that you are going to store both the access token and the refresh token on the client side, usually in the localStorage.
In order to access the protected views on the backend (i.e., the API endpoints that require authentication), you should include the access token in the header of all requests, like this:
http http://127.0.0.1:8000/hello/ "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjAwLCJqdGkiOiJlMGQxZDY2MjE5ODc0ZTY3OWY0NjM0ZWU2NTQ2YTIwMCIsInVzZXJfaWQiOjF9.9eHat3CvRQYnb5EdcgYFzUyMobXzxlAVh_IAgqyvzCE"![]()
You can use this access token for the next five minutes.
After five min, the token will expire, and if you try to access the view again, you are going to get the following error:
http http://127.0.0.1:8000/hello/ "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoiYWNjZXNzIiwiZXhwIjoxNTQ1MjI0MjAwLCJqdGkiOiJlMGQxZDY2MjE5ODc0ZTY3OWY0NjM0ZWU2NTQ2YTIwMCIsInVzZXJfaWQiOjF9.9eHat3CvRQYnb5EdcgYFzUyMobXzxlAVh_IAgqyvzCE"![]()
To get a new access token, you should use the refresh token endpoint /api/token/refresh/ posting the
refresh token:
http post http://127.0.0.1:8000/api/token/refresh/ refresh=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ0b2tlbl90eXBlIjoicmVmcmVzaCIsImV4cCI6MTU0NTMwODIyMiwianRpIjoiNzAyOGFlNjc0ZTdjNDZlMDlmMzUwYjg3MjU1NGUxODQiLCJ1c2VyX2lkIjoxfQ.Md8AO3dDrQBvWYWeZsd_A1J39z6b6HEwWIUZ7ilOiPE![]()
The return is a new access token that you should use in the subsequent requests.
The refresh token is valid for the next 24 hours. When it finally expires too, the user will need to perform a full authentication again using their username and password to get a new set of access token + refresh token.
At first glance the refresh token may look pointless, but in fact it is necessary to make sure the user still have the correct permissions. If your access token have a long expire time, it may take longer to update the information associated with the token. That’s because the authentication check is done by cryptographic means, instead of querying the database and verifying the data. So some information is sort of cached.
There is also a security aspect, in a sense that the refresh token only travel in the POST data. And the access token is sent via HTTP header, which may be logged along the way. So this also give a short window, should your access token be compromised.
This should cover the basics on the backend implementation. It’s worth checking the djangorestframework_simplejwt settings for further customization and to get a better idea of what the library offers.
The implementation on the frontend depends on what framework/library you are using. Some libraries and articles covering popular frontend frameworks like angular/react/vue.js:
The code used in this tutorial is available at github.com/sibtc/drf-jwt-example.
[Django 2.1.3 / Python 3.6.5 / Bootstrap 4.1.3]
In this tutorial we are going to explore some of the Django Crispy Forms features to handle advanced/custom forms rendering. This blog post started as a discussion in our community forum, so I decided to compile the insights and solutions in a blog post to benefit a wider audience.
Table of Contents
Throughout this tutorial we are going to implement the following Bootstrap 4 form using Django APIs:
![]()
This was taken from Bootstrap 4 official documentation as an example of how to use form rows.
NOTE!
The examples below refer to a base.html template. Consider the code below:
base.html
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
</head>
<body>
<div class="container">
{% block content %}
{% endblock %}
</div>
</body>
</html>Install it using pip:
pip install django-crispy-formsAdd it to your INSTALLED_APPS and select which styles to use:
settings.py
INSTALLED_APPS = [
...
'crispy_forms',
]
CRISPY_TEMPLATE_PACK = 'bootstrap4'For detailed instructions about how to install django-crispy-forms, please refer to this tutorial:
How to Use Bootstrap 4 Forms With Django
The Python code required to represent the form above is the following:
from django import forms
STATES = (
('', 'Choose...'),
('MG', 'Minas Gerais'),
('SP', 'Sao Paulo'),
('RJ', 'Rio de Janeiro')
)
class AddressForm(forms.Form):
email = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Email'}))
password = forms.CharField(widget=forms.PasswordInput())
address_1 = forms.CharField(
label='Address',
widget=forms.TextInput(attrs={'placeholder': '1234 Main St'})
)
address_2 = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'Apartment, studio, or floor'})
)
city = forms.CharField()
state = forms.ChoiceField(choices=STATES)
zip_code = forms.CharField(label='Zip')
check_me_out = forms.BooleanField(required=False)In this case I’m using a regular Form, but it could also be a ModelForm based on a Django model with similar
fields. The state field and the STATES choices could be either a foreign key or anything else. Here I’m just using
a simple static example with three Brazilian states.
Template:
{% extends 'base.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
<table>{{ form.as_table }}</table>
<button type="submit">Sign in</button>
</form>
{% endblock %}Rendered HTML:
![]()
Rendered HTML with validation state:
![]()
Same form code as in the example before.
Template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
{% endblock %}Rendered HTML:
![]()
Rendered HTML with validation state:
![]()
Same form code as in the first example.
Template:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post">
{% csrf_token %}
<div class="form-row">
<div class="form-group col-md-6 mb-0">
{{ form.email|as_crispy_field }}
</div>
<div class="form-group col-md-6 mb-0">
{{ form.password|as_crispy_field }}
</div>
</div>
{{ form.address_1|as_crispy_field }}
{{ form.address_2|as_crispy_field }}
<div class="form-row">
<div class="form-group col-md-6 mb-0">
{{ form.city|as_crispy_field }}
</div>
<div class="form-group col-md-4 mb-0">
{{ form.state|as_crispy_field }}
</div>
<div class="form-group col-md-2 mb-0">
{{ form.zip_code|as_crispy_field }}
</div>
</div>
{{ form.check_me_out|as_crispy_field }}
<button type="submit" class="btn btn-primary">Sign in</button>
</form>
{% endblock %}Rendered HTML:
![]()
Rendered HTML with validation state:
![]()
We could use the crispy forms layout helpers to achieve the same result as above. The implementation is done inside
the form __init__ method:
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Layout, Submit, Row, Column
STATES = (
('', 'Choose...'),
('MG', 'Minas Gerais'),
('SP', 'Sao Paulo'),
('RJ', 'Rio de Janeiro')
)
class AddressForm(forms.Form):
email = forms.CharField(widget=forms.TextInput(attrs={'placeholder': 'Email'}))
password = forms.CharField(widget=forms.PasswordInput())
address_1 = forms.CharField(
label='Address',
widget=forms.TextInput(attrs={'placeholder': '1234 Main St'})
)
address_2 = forms.CharField(
widget=forms.TextInput(attrs={'placeholder': 'Apartment, studio, or floor'})
)
city = forms.CharField()
state = forms.ChoiceField(choices=STATES)
zip_code = forms.CharField(label='Zip')
check_me_out = forms.BooleanField(required=False)
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Row(
Column('email', css_class='form-group col-md-6 mb-0'),
Column('password', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
'address_1',
'address_2',
Row(
Column('city', css_class='form-group col-md-6 mb-0'),
Column('state', css_class='form-group col-md-4 mb-0'),
Column('zip_code', css_class='form-group col-md-2 mb-0'),
css_class='form-row'
),
'check_me_out',
Submit('submit', 'Sign in')
)The template implementation is very minimal:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}The end result is the same.
Rendered HTML:
![]()
Rendered HTML with validation state:
![]()
You may also customize the field template and easily reuse throughout your application. Let’s say we want to use the custom Bootstrap 4 checkbox:
![]()
From the official documentation, the necessary HTML to output the input above:
<div class="custom-control custom-checkbox">
<input type="checkbox" class="custom-control-input" id="customCheck1">
<label class="custom-control-label" for="customCheck1">Check this custom checkbox</label>
</div>Using the crispy forms API, we can create a new template for this custom field in our “templates” folder:
custom_checkbox.html
{% load crispy_forms_field %}
<div class="form-group">
<div class="custom-control custom-checkbox">
{% crispy_field field 'class' 'custom-control-input' %}
<label class="custom-control-label" for="{{ field.id_for_label }}">{{ field.label }}</label>
</div>
</div>Now we can create a new crispy field, either in our forms.py module or in a new Python module named fields.py or something.
forms.py
from crispy_forms.layout import Field
class CustomCheckbox(Field):
template = 'custom_checkbox.html'We can use it now in our form definition:
forms.py
class CustomFieldForm(AddressForm):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.layout = Layout(
Row(
Column('email', css_class='form-group col-md-6 mb-0'),
Column('password', css_class='form-group col-md-6 mb-0'),
css_class='form-row'
),
'address_1',
'address_2',
Row(
Column('city', css_class='form-group col-md-6 mb-0'),
Column('state', css_class='form-group col-md-4 mb-0'),
Column('zip_code', css_class='form-group col-md-2 mb-0'),
css_class='form-row'
),
CustomCheckbox('check_me_out'), # <-- Here
Submit('submit', 'Sign in')
)(PS: the AddressForm was defined here and is the same as in the previous example.)
The end result:
![]()
There is much more Django Crispy Forms can do. Hopefully this tutorial gave you some extra insights on how to use the form helpers and layout classes. As always, the official documentation is the best source of information:
Django Crispy Forms layouts docs
Also, the code used in this tutorial is available on GitHub at github.com/sibtc/advanced-crispy-forms-examples.
In this tutorial you are going to learn how to implement Token-based authentication using Django REST Framework (DRF). The token authentication works by exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.
The specifics of how the authentication is handled on the client side vary a lot depending on the technology/language/framework you are working with. The client could be a mobile application using iOS or Android. It could be a desktop application using Python or C++. It could be a Web application using PHP or Ruby.
But once you understand the overall process, it’s easier to find the necessary resources and documentation for your specific use case.
Token authentication is suitable for client-server applications, where the token is safely stored. You should never expose your token, as it would be (sort of) equivalent of a handing out your username and password.
Table of Contents
So let’s start from the very beginning. Install Django and DRF:
pip install django
pip install djangorestframeworkCreate a new Django project:
django-admin.py startproject myapi .Navigate to the myapi folder:
cd myapiStart a new app. I will call my app core:
django-admin.py startapp coreHere is what your project structure should look like:
myapi/
|-- core/
| |-- migrations/
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- tests.py
| +-- views.py
|-- __init__.py
|-- settings.py
|-- urls.py
+-- wsgi.py
manage.pyAdd the core app (you created) and the rest_framework app (you installed) to the INSTALLED_APPS, inside the
settings.py module:
myapi/settings.py
INSTALLED_APPS = [
# Django Apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-Party Apps
'rest_framework',
# Local Apps (Your project's apps)
'myapi.core',
]Return to the project root (the folder where the manage.py script is), and migrate the database:
python manage.py migrateLet’s create our first API view just to test things out:
myapi/core/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
class HelloView(APIView):
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)Now register a path in the urls.py module:
myapi/urls.py
from django.urls import path
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
]So now we have an API with just one endpoint /hello/ that we can perform GET requests. We can use the browser to
consume this endpoint, just by accessing the URL http://127.0.0.1:8000/hello/:
![]()
We can also ask to receive the response as plain JSON data by passing the format parameter in the querystring like
http://127.0.0.1:8000/hello/?format=json:
![]()
Both methods are fine to try out a DRF API, but sometimes a command line tool is more handy as we can play more easily with the requests headers. You can use cURL, which is widely available on all major Linux/macOS distributions:
curl http://127.0.0.1:8000/hello/![]()
But usually I prefer to use HTTPie, which is a pretty awesome Python command line tool:
http http://127.0.0.1:8000/hello/![]()
Now let’s protect this API endpoint so we can implement the token authentication:
myapi/core/views.py
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import IsAuthenticated # <-- Here
class HelloView(APIView):
permission_classes = (IsAuthenticated,) # <-- And here
def get(self, request):
content = {'message': 'Hello, World!'}
return Response(content)Try again to access the API endpoint:
http http://127.0.0.1:8000/hello/![]()
And now we get an HTTP 403 Forbidden error. Now let’s implement the token authentication so we can access this endpoint.
We need to add two pieces of information in our settings.py module. First include rest_framework.authtoken to
your INSTALLED_APPS and include the TokenAuthentication to REST_FRAMEWORK:
myapi/settings.py
INSTALLED_APPS = [
# Django Apps
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Third-Party Apps
'rest_framework',
'rest_framework.authtoken', # <-- Here
# Local Apps (Your project's apps)
'myapi.core',
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication', # <-- And here
],
}Migrate the database to create the table that will store the authentication tokens:
python manage.py migrate![]()
Now we need a user account. Let’s just create one using the manage.py command line utility:
python manage.py createsuperuser --username vitor --email vitor@example.comThe easiest way to generate a token, just for testing purpose, is using the command line utility again:
python manage.py drf_create_token vitor![]()
This piece of information, the random string 9054f7aa9305e012b3c2300408c3dfdf390fcddf is what we are going to use
next to authenticate.
But now that we have the TokenAuthentication in place, let’s try to make another request to our /hello/ endpoint:
http http://127.0.0.1:8000/hello/![]()
Notice how our API is now providing some extra information to the client on the required authentication method.
So finally, let’s use our token!
http http://127.0.0.1:8000/hello/ 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'![]()
And that’s pretty much it. For now on, on all subsequent request you should include the header Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf.
The formatting looks weird and usually it is a point of confusion on how to set this header. It will depend on the client and how to set the HTTP request header.
For example, if we were using cURL, the command would be something like this:
curl http://127.0.0.1:8000/hello/ -H 'Authorization: Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'Or if it was a Python requests call:
import requests
url = 'http://127.0.0.1:8000/hello/'
headers = {'Authorization': 'Token 9054f7aa9305e012b3c2300408c3dfdf390fcddf'}
r = requests.get(url, headers=headers)Or if we were using Angular, you could implement an HttpInterceptor and set a header:
import { Injectable } from '@angular/core';
import { HttpRequest, HttpHandler, HttpEvent, HttpInterceptor } from '@angular/common/http';
import { Observable } from 'rxjs';
@Injectable()
export class AuthInterceptor implements HttpInterceptor {
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
const user = JSON.parse(localStorage.getItem('user'));
if (user && user.token) {
request = request.clone({
setHeaders: {
Authorization: `Token ${user.accessToken}`
}
});
}
return next.handle(request);
}
}The DRF provide an endpoint for the users to request an authentication token using their username and password.
Include the following route to the urls.py module:
myapi/urls.py
from django.urls import path
from rest_framework.authtoken.views import obtain_auth_token # <-- Here
from myapi.core import views
urlpatterns = [
path('hello/', views.HelloView.as_view(), name='hello'),
path('api-token-auth/', obtain_auth_token, name='api_token_auth'), # <-- And here
]So now we have a brand new API endpoint, which is /api-token-auth/. Let’s first inspect it:
http http://127.0.0.1:8000/api-token-auth/![]()
It doesn’t handle GET requests. Basically it’s just a view to receive a POST request with username and password.
Let’s try again:
http post http://127.0.0.1:8000/api-token-auth/ username=vitor password=123![]()
The response body is the token associated with this particular user. After this point you store this token and apply it to the future requests.
Then, again, the way you are going to make the POST request to the API depends on the language/framework you are using.
If this was an Angular client, you could store the token in the localStorage, if this was a Desktop CLI application
you could store in a text file in the user’s home directory in a dot file.
Hopefully this tutorial provided some insights on how the token authentication works. I will try to follow up this tutorial providing some concrete examples of Angular applications, command line applications and Web clients as well.
It is important to note that the default Token implementation has some limitations such as only one token per user, no built-in way to set an expiry date to the token.
You can grab the code used in this tutorial at github.com/sibtc/drf-token-auth-example.
Django comes with a variety of command line utilities that can be either invoked using django-admin.py or the
convenient manage.py script. A nice thing about it is that you can also add your own commands. Those management
commands can be very handy when you need to interact with your application via command line using a terminal and it can
also serve as an interface to execute cron jobs. In this tutorial you are going to learn how to code your own commands.
Just before we get started, let’s take a moment to familiarize with Django’s command line interface. You are probably
already familiar with commands like startproject, runserver or collectstatic. To see a complete list of commands
you can run the command below:
python manage.py helpOutput:
Type 'manage.py help <subcommand>' for help on a specific subcommand.
Available subcommands:
[auth]
changepassword
createsuperuser
[contenttypes]
remove_stale_contenttypes
[django]
check
compilemessages
createcachetable
dbshell
diffsettings
dumpdata
flush
inspectdb
loaddata
makemessages
makemigrations
migrate
sendtestemail
shell
showmigrations
sqlflush
sqlmigrate
sqlsequencereset
squashmigrations
startapp
startproject
test
testserver
[sessions]
clearsessions
[staticfiles]
collectstatic
findstatic
runserver
We can create our own commands for our apps and include them in the list by creating a management/commands directory inside an app directory, like below:
mysite/ <-- project directory
|-- core/ <-- app directory
| |-- management/
| | +-- commands/
| | +-- my_custom_command.py <-- module where command is going to live
| |-- migrations/
| | +-- __init__.py
| |-- __init__.py
| |-- admin.py
| |-- apps.py
| |-- models.py
| |-- tests.py
| +-- views.py
|-- mysite/
| |-- __init__.py
| |-- settings.py
| |-- urls.py
| |-- wsgi.py
+-- manage.pyThe name of the command file will be used to invoke using the command line utility. For example, if our command was
called my_custom_command.py, then we will be able to execute it via:
python manage.py my_custom_commandLet’s explore next our first example.
Below, a basic example of what the custom command should look like:
management/commands/what_time_is_it.py
from django.core.management.base import BaseCommand
from django.utils import timezone
class Command(BaseCommand):
help = 'Displays current time'
def handle(self, *args, **kwargs):
time = timezone.now().strftime('%X')
self.stdout.write("It's now %s" % time)Basically a Django management command is composed by a class named Command which inherits from BaseCommand. The
command code should be defined inside the handle() method.
See how we named our module what_time_is_it.py. This command can be executed as:
python manage.py what_time_is_itOutput:
It's now 18:35:31You may be asking yourself, how is that different from a regular Python script, or what’s the benefit of it. Well, the main advantage is that all Django machinery is loaded and ready to be used. That means you can import models, execute queries to the database using Django’s ORM and interact with all your project’s resources.
Django make use of the argparse, which is part of Python’s standard
library. To handle arguments in our custom command we should define a method named add_arguments.
The next example is a command that create random user instances. It takes a mandatory argument named total, which
will define the number of users that will be created by the command.
management/commands/create_users.py
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string
class Command(BaseCommand):
help = 'Create random users'
def add_arguments(self, parser):
parser.add_argument('total', type=int, help='Indicates the number of users to be created')
def handle(self, *args, **kwargs):
total = kwargs['total']
for i in range(total):
User.objects.create_user(username=get_random_string(), email='', password='123')Here is how one would use it:
python manage.py create_users 10The optional (and named) arguments can be passed in any order. In the example below you will find the definition of an argument named “prefix”, which will be used to compose the username field:
management/commands/create_users.py
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string
class Command(BaseCommand):
help = 'Create random users'
def add_arguments(self, parser):
parser.add_argument('total', type=int, help='Indicates the number of users to be created')
# Optional argument
parser.add_argument('-p', '--prefix', type=str, help='Define a username prefix', )
def handle(self, *args, **kwargs):
total = kwargs['total']
prefix = kwargs['prefix']
for i in range(total):
if prefix:
username = '{prefix}_{random_string}'.format(prefix=prefix, random_string=get_random_string())
else:
username = get_random_string()
User.objects.create_user(username=username, email='', password='123')Usage:
python manage.py create_users 10 --prefix custom_useror
python manage.py create_users 10 -p custom_userIf the prefix is used, the username field will be created as custom_user_oYwoxtt4vNHR. If not prefix, it will be
created simply as oYwoxtt4vNHR – a random string.
Another type of optional arguments are flags, which are used to handle boolean values. Let’s say we want to add an
--admin flag, to instruct our command to create a super user or to create a regular user if the flag is not present.
management/commands/create_users.py
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
from django.utils.crypto import get_random_string
class Command(BaseCommand):
help = 'Create random users'
def add_arguments(self, parser):
parser.add_argument('total', type=int, help='Indicates the number of users to be created')
parser.add_argument('-p', '--prefix', type=str, help='Define a username prefix')
parser.add_argument('-a', '--admin', action='store_true', help='Create an admin account')
def handle(self, *args, **kwargs):
total = kwargs['total']
prefix = kwargs['prefix']
admin = kwargs['admin']
for i in range(total):
if prefix:
username = '{prefix}_{random_string}'.format(prefix=prefix, random_string=get_random_string())
else:
username = get_random_string()
if admin:
User.objects.create_superuser(username=username, email='', password='123')
else:
User.objects.create_user(username=username, email='', password='123')Usage:
python manage.py create_users 2 --adminOr
python manage.py create_users 2 -aLet’s create a new command now named delete_users. In this new command we will be able to pass a list of user ids and the command should delete those users from the database.
management/commands/delete_users.py
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Delete users'
def add_arguments(self, parser):
parser.add_argument('user_id', nargs='+', type=int, help='User ID')
def handle(self, *args, **kwargs):
users_ids = kwargs['user_id']
for user_id in users_ids:
try:
user = User.objects.get(pk=user_id)
user.delete()
self.stdout.write('User "%s (%s)" deleted with success!' % (user.username, user_id))
except User.DoesNotExist:
self.stdout.write('User with id "%s" does not exist.' % user_id)Usage:
python manage.py delete_users 1Output:
User "SMl5ISqAsIS8 (1)" deleted with success!We can also pass a number of ids separated by spaces, so the command will delete the users in a single call:
python manage.py delete_users 1 2 3 4Output:
User with id "1" does not exist.
User "9teHR4Y7Bz4q (2)" deleted with success!
User "ABdSgmBtfO2t (3)" deleted with success!
User "BsDxOO8Uxgvo (4)" deleted with success!We could improve the previous example a little big by setting an appropriate color to the output message:
management/commands/delete_users.py
from django.contrib.auth.models import User
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Delete users'
def add_arguments(self, parser):
parser.add_argument('user_id', nargs='+', type=int, help='User ID')
def handle(self, *args, **kwargs):
users_ids = kwargs['user_id']
for user_id in users_ids:
try:
user = User.objects.get(pk=user_id)
user.delete()
self.stdout.write(self.style.SUCCESS('User "%s (%s)" deleted with success!' % (user.username, user_id)))
except User.DoesNotExist:
self.stdout.write(self.style.WARNING('User with id "%s" does not exist.' % user_id))Usage is the same as before, difference now is just the output:
python manage.py delete_users 3 4 5 6Output:
![]()
Below a list of all available styles, in form of a management command:
from django.core.management.base import BaseCommand
class Command(BaseCommand):
help = 'Show all available styles'
def handle(self, *args, **kwargs):
self.stdout.write(self.style.ERROR('error - A major error.'))
self.stdout.write(self.style.NOTICE('notice - A minor error.'))
self.stdout.write(self.style.SUCCESS('success - A success.'))
self.stdout.write(self.style.WARNING('warning - A warning.'))
self.stdout.write(self.style.SQL_FIELD('sql_field - The name of a model field in SQL.'))
self.stdout.write(self.style.SQL_COLTYPE('sql_coltype - The type of a model field in SQL.'))
self.stdout.write(self.style.SQL_KEYWORD('sql_keyword - An SQL keyword.'))
self.stdout.write(self.style.SQL_TABLE('sql_table - The name of a model in SQL.'))
self.stdout.write(self.style.HTTP_INFO('http_info - A 1XX HTTP Informational server response.'))
self.stdout.write(self.style.HTTP_SUCCESS('http_success - A 2XX HTTP Success server response.'))
self.stdout.write(self.style.HTTP_NOT_MODIFIED('http_not_modified - A 304 HTTP Not Modified server response.'))
self.stdout.write(self.style.HTTP_REDIRECT('http_redirect - A 3XX HTTP Redirect server response other than 304.'))
self.stdout.write(self.style.HTTP_NOT_FOUND('http_not_found - A 404 HTTP Not Found server response.'))
self.stdout.write(self.style.HTTP_BAD_REQUEST('http_bad_request - A 4XX HTTP Bad Request server response other than 404.'))
self.stdout.write(self.style.HTTP_SERVER_ERROR('http_server_error - A 5XX HTTP Server Error response.'))
self.stdout.write(self.style.MIGRATE_HEADING('migrate_heading - A heading in a migrations management command.'))
self.stdout.write(self.style.MIGRATE_LABEL('migrate_label - A migration name.'))![]()
If you have a task that must run periodically, like generating a report every Monday. Or let’s say you have a Web scrapper that collects data from some Website every 10 minutes. You can define this code as a management command and simply add it to your server’s crontab like this:
# m h dom mon dow command
0 4 * * * /home/mysite/venv/bin/python /home/mysite/mysite/manage.py my_custom_commandThe example above will execute the my_custom_command every day at 4 a.m.
The examples above should be enough to get you started. More advanced usage will boil down to knowing how to use the argparse features. And of course, Django’s official documentation on management commands is the best resource.
You can find all the code used in this tutorial on GitHub.
This is a quick tutorial to get you start with django-crispy-forms and never look back. Crispy-forms is a great application that gives you control over how you render Django forms, without breaking the default behavior. This tutorial is going to be tailored towards Bootstrap 4, but it can also be used with older Bootstrap versions as well as with the Foundation framework.
The main reason why I like to use it on my projects is because you can simply render a Django form using `` and it will be nicely rendered with Bootstrap 4, with very minimal setup. It’s a really life saver.
Install it using pip:
pip install django-crispy-formsAdd it to your INSTALLED_APPS and select which styles to use:
settings.py
INSTALLED_APPS = [
...
'crispy_forms',
]
CRISPY_TEMPLATE_PACK = 'bootstrap4'You can either download the latest Bootstrap 4 version at getbootstrap.com. In that case, go to download page and get the Compiled CSS and JS version.
Or you can use the hosted Bootstrap CDN:
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity="sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin="anonymous"></script>For simplicity, I will be using the CDN version. Here is my base.html template that will be referenced in the following examples:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<title>Django People</title>
</head>
<body>
<div class="container">
<div class="row justify-content-center">
<div class="col-8">
<h1 class="mt-2">Django People</h1>
<hr class="mt-0 mb-4">
{% block content %}
{% endblock %}
</div>
</div>
</div>
</body>
</html>I only added the CSS file because we won’t be using any JavaScript feature.
Suppose we have a model named Person as follows:
models.py
from django.db import models
class Person(models.Model):
name = models.CharField(max_length=130)
email = models.EmailField(blank=True)
job_title = models.CharField(max_length=30, blank=True)
bio = models.TextField(blank=True)Let’s say we wanted to create a view to add new Person objects. In that case we could use the built-in CreateView:
views.py
from django.views.generic import CreateView
from .models import Person
class PersonCreateView(CreateView):
model = Person
fields = ('name', 'email', 'job_title', 'bio')Without any further change, Django will try to use a template named people/person_form.html. In that case “people”
is the name of my Django app:
people/person_form.html
{% extends 'base.html' %}
{% block content %}
<form method="post">
{% csrf_token %}
{{ form }}
<button type="submit" class="btn btn-success">Save person</button>
</form>
{% endblock %}This is a very basic form rendering, and as it is, Django will render it like this, with no style, just plain form fields:
![]()
To render the same form using Bootstrap 4 CSS classes you can do the following:
people/person_form.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
<form method="post" novalidate>
{% csrf_token %}
{{ form|crispy }}
<button type="submit" class="btn btn-success">Save person</button>
</form>
{% endblock %}Now the result, much better:
![]()
There are some cases where you may want more freedom to render your fields. You can do so by rendering the fields
manually and using the as_crispy_field template filter:
{% extends 'base.html' %}
{% load crispy_forms_tags %}
**people/person_form.html**
{% block content %}
<form method="post" novalidate>
{% csrf_token %}
<div class="row">
<div class="col-6">
{{ form.name|as_crispy_field }}
</div>
<div class="col-6">
{{ form.email|as_crispy_field }}
</div>
</div>
{{ form.job_title|as_crispy_field }}
{{ form.bio|as_crispy_field }}
<button type="submit" class="btn btn-success">Save person</button>
</form>
{% endblock %}And the result is something like the screen shot below:
![]()
The django-crispy-forms app have a special class named FormHelper to make your life easier and to give you complete
control over how you want to render your forms.
Here is an example of an update view:
forms.py
from django import forms
from crispy_forms.helper import FormHelper
from crispy_forms.layout import Submit
from people.models import Person
class PersonForm(forms.ModelForm):
class Meta:
model = Person
fields = ('name', 'email', 'job_title', 'bio')
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.helper = FormHelper()
self.helper.form_method = 'post'
self.helper.add_input(Submit('submit', 'Save person'))The job is done inside the __init__() method. The rest is just a regular Django model form. Here I’m defining that
this form should handle the request using the POST method and the form should have an submit button with label
“Save person”.
Now our view, just regular Django code:
views.py
from django.views.generic import UpdateView
from people.models import Person
from people.forms import PersonForm
class PersonUpdateView(UpdateView):
model = Person
form_class = PersonForm
template_name = 'people/person_update_form.html'Then in our template:
people/person_update_form.html
{% extends 'base.html' %}
{% load crispy_forms_tags %}
{% block content %}
{% crispy form %}
{% endblock %}Here we can simply call the {% crispy %} template tag and pass our form instance as parameter.
And that’s all you need to render the form:
![]()
That’s pretty much it for the basics. Honestly that’s about all that I use. Usually I don’t even go for the
FormHelper objects. But there are much more about it. If you are interested, you can check their official
documentation: django-crispy-forms.readthedocs.io.
If you are not sure about where you should create a certain file, or want to explore the sample project I created for this tutorial, you can grab the source code on GitHub at github.com/sibtc/bootstrap-forms-example.
Highcharts is, in my opinion, one of the best JavaScript libraries to work with data visualization and charts out there. Even though Highcharts is open source, it’s a commercial library. It’s free for use in non-commercial applications though.
In this tutorial we are going to explore how to integrate it with a Django project to render dynamically generated charts. In relation to drawing the charts and rendering it to the client, all the hard work is done by Highcharts at the client side. The configuration and setup is pure JavaScript.
The main challenge here is on how to translate the data from your backend to a format that Highcharts will understand. This data may come from a database or an external API, and probably is represented as Python objects (like in a QuerySet), or simply be represented as Python dictionaries or lists.
Generally speaking, there are two ways to do it:
The first option is like a brute force and in many cases the easiest way. The second option requires a slightly complicated setup, but you will also benefit from the page loading speed and from the maintainability of the code.
Basically we just need to include the Highcharts library in our template and we are ready to go. You can either download and serve it locally or simply use their CDN:
<script src="https://code.highcharts.com/highcharts.src.js"></script>Now we need some data. I thought that it would be fun to play with an existing dataset. The Titanic dataset is pretty famous one, and easy to access.
What I did here was loading the dataset (1300~ rows) into a model named Passenger:
class Passenger(models.Model):
name = models.CharField()
sex = models.CharField()
survived = models.BooleanField()
age = models.FloatField()
ticket_class = models.PositiveSmallIntegerField()
embarked = models.CharField()If you are familiar with data mining, data science, or machine learning probably you already know this data set. This dataset is usually used for learning purpose. It’s composed by the list of passengers of the famous RMS Titanic tragedy.
We won’t be doing anything smart with it, just querying the database and displaying the data using Highcharts.
I won’t dive into deep details about Highcharts. The goal is to understand how to make Django and Highcharts talk. For details about how to do this or that with Highcharts, best thing is to consult the official documentation.
Here is a working example of a column chart using Highcharts:
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Historic World Population by Region'
},
xAxis: {
categories: ['Africa', 'America', 'Asia', 'Europe', 'Oceania']
},
series: [{
name: 'Year 1800',
data: [107, 31, 635, 203, 2]
}, {
name: 'Year 1900',
data: [133, 156, 947, 408, 6]
}, {
name: 'Year 2012',
data: [1052, 954, 4250, 740, 38]
}]
});
</script>
</body>
</html>The code above generates the following chart:
![]()
The basic structure here is:
Highcharts.chart('id_of_the_container', {
// dictionary of options/configuration
});The most straightforward way to do it is by writing directly in the template (which is not recommended).
Let’s write an query to display the number of survivors and deaths organized by ticket class.
views.py
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
return render(request, 'ticket_class.html', {'dataset': dataset})The queryset above generates a data in the following format:
[
{'ticket_class': 1, 'survived_count': 200, 'not_survived_count': 123},
{'ticket_class': 2, 'survived_count': 119, 'not_survived_count': 158},
{'ticket_class': 3, 'survived_count': 181, 'not_survived_count': 528}
]Then we could just write it in the template, inside the JavaScript tags:
ticket_class.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Titanic Survivors by Ticket Class'
},
xAxis: {
categories: [
{% for entry in dataset %}'{{ entry.ticket_class }} Class'{% if not forloop.last %}, {% endif %}{% endfor %}
]
},
series: [{
name: 'Survived',
data: [
{% for entry in dataset %}{{ entry.survived_count }}{% if not forloop.last %}, {% endif %}{% endfor %}
],
color: 'green'
}, {
name: 'Not survived',
data: [
{% for entry in dataset %}{{ entry.not_survived_count }}{% if not forloop.last %}, {% endif %}{% endfor %}
],
color: 'red'
}]
});
</script>![]()
This kind of strategy is not really a good idea because the code is hard to read, hard to maintain and it is too easy
to shoot in the foot. Because we are using Python to generate JavaScript code, we have to format it properly.
For example, the code {% if not forloop.last %}, {% endif %} is to not append a comma (,) after
the last item of the array (otherwise the result would be [200, 119, 181,]). The newest JavaScript versions are
forgiving and accepts an extra comma (like Python does), but older versions don’t, so it might cause problem in old
browsers. Anyway, the point is you have to make sure your Python code is writing valid JavaScript code.
A slightly better way to do it would be processing the data a little bit more in the view:
views.py
import json
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view_2(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
categories = list()
survived_series = list()
not_survived_series = list()
for entry in dataset:
categories.append('%s Class' % entry['ticket_class'])
survived_series.append(entry['survived_count'])
not_survived_series.append(entry['not_survived_count'])
return render(request, 'ticket_class_2.html', {
'categories': json.dumps(categories),
'survived_series': json.dumps(survived_series),
'not_survived_series': json.dumps(not_survived_series)
})ticket_class_2.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {
chart: {
type: 'column'
},
title: {
text: 'Titanic Survivors by Ticket Class'
},
xAxis: {
categories: {{ categories|safe }}
},
series: [{
name: 'Survived',
data: {{ survived_series }},
color: 'green'
}, {
name: 'Not survived',
data: {{ not_survived_series }},
color: 'red'
}]
});
</script>Here’s what we are doing: first run through the queryset and create three separate lists, append the values and do the
formatting. After that, use the json module and dump the Python lists into JSON format. The result are Python strings
properly formatted as JSON data.
We have to use the safe template filter to properly render the categories because Django automatically escape
characters like ' and " for safety reason, so we have to instruct Django to trust and render it as it is.
We could also do all the configuration in the backend, like this:
views.py
import json
from django.db.models import Count, Q
from django.shortcuts import render
from .models import Passenger
def ticket_class_view_3(request):
dataset = Passenger.objects \
.values('ticket_class') \
.annotate(survived_count=Count('ticket_class', filter=Q(survived=True)),
not_survived_count=Count('ticket_class', filter=Q(survived=False))) \
.order_by('ticket_class')
categories = list()
survived_series_data = list()
not_survived_series_data = list()
for entry in dataset:
categories.append('%s Class' % entry['ticket_class'])
survived_series_data.append(entry['survived_count'])
not_survived_series_data.append(entry['not_survived_count'])
survived_series = {
'name': 'Survived',
'data': survived_series_data,
'color': 'green'
}
not_survived_series = {
'name': 'Survived',
'data': not_survived_series_data,
'color': 'red'
}
chart = {
'chart': {'type': 'column'},
'title': {'text': 'Titanic Survivors by Ticket Class'},
'xAxis': {'categories': categories},
'series': [survived_series, not_survived_series]
}
dump = json.dumps(chart)
return render(request, 'ticket_class_3.html', {'chart': dump})ticket_class_3.html
<div id="container"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script>
Highcharts.chart('container', {{ chart|safe }});
</script>As you can see, that way we move all the configuration to the server side. But we are still interacting with the JavaScript code directly.
Now this is how I usually like to work with Highcharts (or any other JavaScript library that interacts with the server).
The idea here is to render the chart using an asynchronous call, returning a JsonResponse from the server.
This time, we are going to need two routes:
urls.py
from django.urls import path
from passengers import views
urlpatterns = [
path('json-example/', views.json_example, name='json_example'),
path('json-example/data/', views.chart_data, name='chart_data'),
]The json_example URL route is pointing to a regular view, which will render the template which will invoke the
chart_data view. This call can be automatic upon page load, or it can be triggered by an action (a button click
for example).
views.py
def json_example(request):
return render(request, 'json_example.html')
def chart_data(request):
dataset = Passenger.objects \
.values('embarked') \
.exclude(embarked='') \
.annotate(total=Count('embarked')) \
.order_by('embarked')
port_display_name = dict()
for port_tuple in Passenger.PORT_CHOICES:
port_display_name[port_tuple[0]] = port_tuple[1]
chart = {
'chart': {'type': 'pie'},
'title': {'text': 'Titanic Survivors by Ticket Class'},
'series': [{
'name': 'Embarkation Port',
'data': list(map(lambda row: {'name': port_display_name[row['embarked']], 'y': row['total']}, dataset))
}]
}
return JsonResponse(chart)Here we can see the json_example is nothing special, just returning the json_example.html template, which we are
going to explore in a minute.
The chart_data is the one doing all the hard work. Here we have the database query and building the chart
dictionary. In the end we return the chart data as a JSON object.
json_example.html
<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>Django Highcharts Example</title>
</head>
<body>
<div id="container" data-url="{% url 'chart_data' %}"></div>
<script src="https://code.highcharts.com/highcharts.src.js"></script>
<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script>
$.ajax({
url: $("#container").attr("data-url"),
dataType: 'json',
success: function (data) {
Highcharts.chart("container", data);
}
});
</script>
</body>
</html>Here is where the magic happens. The div with id container is where the chart is going to be rendered. Now, observe
that I included a custom attribute named data-url. Inside this attribute I stored the path to the view that will be
used to load the chart data.
Inside the ajax call, we make the request based on the URL provided in the data-url and instruct the ajax request
that we are expecting a JSON object in return (defined by the dataType). When the request completes, the JSON
response will be inside the data parameter in the success function. Finally, inside the success function, we
render the chart using the Highcharts API.
This is extremely useful because now we can decouple all the JavaScript from our template. In this example we used a single file for simplicity, but nothing stops us now from saving the script tag content in a separate file. This is great because we are no longer mixing the Django template language with JavaScript.
The result is the following screen shot:
![]()
In this tutorial we explored the basics on how to integrate Highcharts.js with Django. The implementation concepts used in this tutorial can be applied in other charts libraries such as Charts.js. The process should be very similar.
Whenever possible, try to avoid interacting with JavaScript code using the Django Template Language. Prefer returning the data as JSON objects already processed and ready to use.
Usually when working with charts and data visualization the most challenging part is to squeeze the data in the format required to render the chart. What I usually do is first create a static example hardcoding the data so I can have an idea about the data format. Next, I start creating the QuerySet using in the Python terminal. After I get it right, I finally write the view function.
If you want to learn more, the best way is to get your hands dirty. Here’s something you can do: