|
| 1 | +title: Quickly Use Bootstrap 4 in a Django Template with a CDN |
| 2 | +slug: bootstrap-4-django-template |
| 3 | +meta: Use the Bootstrap 4 CDN to quickly add Bootstrap styling and functionality to Django web apps. |
| 4 | +category: post |
| 5 | +date: 2020-07-05 |
| 6 | +modified: 2020-07-05 |
| 7 | +newsletter: False |
| 8 | +headerimage: /img/headers/django-bootstrap.jpg |
| 9 | +headeralt: Logos for the implementations used in this blog post. Copyright their respective owners. |
| 10 | + |
| 11 | + |
| 12 | +The [Django](/django.html) [web framework](/web-frameworks.html) |
| 13 | +makes it easy to render HTML using the [Django template engine](/django-templates.html). |
| 14 | +However, the default styling on HTML pages usually need a |
| 15 | +[Cascading Style Sheet (CSS)](/cascading-style-sheets.html) framework such as |
| 16 | +Bootstrap to make the design look decent. |
| 17 | +In this beginner's tutorial, we'll use the [Bootstrap](/bootstrap-css.html) |
| 18 | +[Content Delivery Network (CDN)](/content-delivery-networks-cdns.html) |
| 19 | +to quickly add Bootstrap to a rendered HTML page. |
| 20 | + |
| 21 | +Here is what the `<h1>` element styling will look like at the end |
| 22 | +of this tutorial: |
| 23 | + |
| 24 | +<img src="/img/200705-bootstrap-4-django-template/hello-world-bootstrap-style.jpg" width="100%" class="shot rnd outl" alt="Bootstrap-enhanced HTML page saying 'Hello, world!'."> |
| 25 | + |
| 26 | + |
| 27 | +## Tutorial Requirements |
| 28 | +Throughout this tutorial we are going to use the following dependencies, |
| 29 | +which we will install in just a moment. Make sure you also have Python 3, |
| 30 | +[preferrably 3.7 or newer installed](https://www.python.org/downloads/), |
| 31 | +in your environment: |
| 32 | + |
| 33 | +We will use the following dependencies to complete this |
| 34 | +tutorial: |
| 35 | + |
| 36 | +* [Django](/django.html) [web framework](/web-frameworks.html), |
| 37 | + [version 3.0.8](https://www.djangoproject.com/download/) |
| 38 | + |
| 39 | +All code in this blog post is available open source under the MIT license |
| 40 | +on GitHub under the |
| 41 | +[bootstrap-4-django-template directory of the blog-code-examples repository](https://github.com/fullstackpython/blog-code-examples). |
| 42 | +Use the source code as you desire for your own projects. |
| 43 | + |
| 44 | + |
| 45 | +## Development environment set up |
| 46 | +Change into the directory where you keep your Python |
| 47 | +[virtual environments](/virtual-environments-virtualenvs-venvs.html). |
| 48 | +Create a new virtualenv for this project using the following |
| 49 | +command. |
| 50 | + |
| 51 | +Start the Django project by creating a new |
| 52 | +[virtual environment](/virtual-environments-virtualenvs-venvs.html) |
| 53 | +using the following command. I recommend using a separate directory |
| 54 | +such as `~/venvs/` (the tilde is a shortcut for your user's `home` |
| 55 | +directory) so that you always know where all your virtualenvs are |
| 56 | +located. |
| 57 | + |
| 58 | +```bash |
| 59 | +python3 -m venv ~/venvs/djbootstrap4 |
| 60 | +``` |
| 61 | + |
| 62 | +Activate the virtualenv with the `activate` shell script: |
| 63 | + |
| 64 | +```bash |
| 65 | +source ~/venvs/djbootstrap4/bin/activate |
| 66 | +``` |
| 67 | + |
| 68 | +After the above command is executed, the command prompt will |
| 69 | +change so that the name of the virtualenv is prepended to the |
| 70 | +original command prompt format, so if your prompt is simply |
| 71 | +`$`, it will now look like the following: |
| 72 | + |
| 73 | +```bash |
| 74 | +(djbootstrap4) $ |
| 75 | +``` |
| 76 | + |
| 77 | +Remember, you have to activate your virtualenv in every new terminal |
| 78 | +window where you want to use dependencies in the virtualenv. |
| 79 | + |
| 80 | +We can now install the [Django](https://pypi.org/project/Django/) |
| 81 | +package into the activated but otherwise empty virtualenv. |
| 82 | + |
| 83 | +``` |
| 84 | +pip install django==3.0.8 |
| 85 | +``` |
| 86 | + |
| 87 | +Look for output similar to the following to confirm the appropriate |
| 88 | +packages were installed correctly from PyPI. |
| 89 | + |
| 90 | +``` |
| 91 | +Collecting django |
| 92 | + Using cached https://files.pythonhosted.org/packages/ca/ab/5e004afa025a6fb640c6e983d4983e6507421ff01be224da79ab7de7a21f/Django-3.0.8-py3-none-any.whl |
| 93 | +Collecting sqlparse>=0.2.2 (from django) |
| 94 | + Using cached https://files.pythonhosted.org/packages/85/ee/6e821932f413a5c4b76be9c5936e313e4fc626b33f16e027866e1d60f588/sqlparse-0.3.1-py2.py3-none-any.whl |
| 95 | +Collecting asgiref~=3.2 (from django) |
| 96 | + Using cached https://files.pythonhosted.org/packages/d5/eb/64725b25f991010307fd18a9e0c1f0e6dff2f03622fc4bcbcdb2244f60d6/asgiref-3.2.10-py3-none-any.whl |
| 97 | +Collecting pytz (from django) |
| 98 | + Using cached https://files.pythonhosted.org/packages/4f/a4/879454d49688e2fad93e59d7d4efda580b783c745fd2ec2a3adf87b0808d/pytz-2020.1-py2.py3-none-any.whl |
| 99 | +Installing collected packages: sqlparse, asgiref, pytz, django |
| 100 | +Successfully installed asgiref-3.2.10 django-3.0.8 pytz-2020.1 sqlparse-0.3.1 |
| 101 | +``` |
| 102 | + |
| 103 | +We can get started coding the application now that we have all of our |
| 104 | +required dependencies installed. |
| 105 | + |
| 106 | + |
| 107 | +## Building our application |
| 108 | +Let's begin coding our application. |
| 109 | + |
| 110 | +We can use the [Django](/django.html) `django-admin` tool to create |
| 111 | +the boilerplate code structure to get our project started. |
| 112 | +Change into the directory where you develop your applications. For |
| 113 | +example, I typically use `/Users/matt/devel/py/` for all of my |
| 114 | +Python projects. Then run the following command to start a Django |
| 115 | +project named `djbootstrap4`: |
| 116 | + |
| 117 | +``` |
| 118 | +django-admin.py startproject djbootstrap4 |
| 119 | +``` |
| 120 | + |
| 121 | +Note that in this tutorial we are using the same name for both the |
| 122 | +virtualenv and the Django project directory, but they can be |
| 123 | +different names if you prefer that for organizing your own projects. |
| 124 | + |
| 125 | +The `django-admin` command creates a directory named `djbootstrap4` |
| 126 | +along with several subdirectories that you should be familiar with |
| 127 | +if you have previously worked with Django. |
| 128 | + |
| 129 | +Change directories into the new project. |
| 130 | + |
| 131 | +``` |
| 132 | +cd djbootstrap4 |
| 133 | +``` |
| 134 | + |
| 135 | +Create a new Django app within `djbootstrap4`. |
| 136 | + |
| 137 | +``` |
| 138 | +python manage.py startapp bootstrap4 |
| 139 | +``` |
| 140 | + |
| 141 | +Django will generate a new folder named `bootstrap4` for the project. |
| 142 | +We should update the URLs so the app is accessible before we write |
| 143 | +our `views.py` code. |
| 144 | + |
| 145 | +Open `djbootstrap4/djbootstrap4/urls.py`. Add the highlighted |
| 146 | +lines so that URL resolver will check the `bootstrap4` app |
| 147 | +for additional routes to match with URLs that are requested of |
| 148 | +this Django application. |
| 149 | + |
| 150 | +```python |
| 151 | +# djbootstrap4/djbootstrap4/urls.py |
| 152 | +~~from django.conf.urls import include |
| 153 | +from django.contrib import admin |
| 154 | +from django.urls import path |
| 155 | + |
| 156 | + |
| 157 | +urlpatterns = [ |
| 158 | +~~ path('', include('bootstrap4.urls')), |
| 159 | + path('admin/', admin.site.urls), |
| 160 | +] |
| 161 | +``` |
| 162 | + |
| 163 | +Save `djbootstrap4/djbootstrap4/urls.py` and open |
| 164 | +`djbootstrap4/djbootstrap4/settings.py`. |
| 165 | +Add the `bootstrap4` app to `settings.py` by inserting |
| 166 | +the highlighted line: |
| 167 | + |
| 168 | +```python |
| 169 | +# djbootstrap4/djbootstrap4/settings.py |
| 170 | +# Application definition |
| 171 | + |
| 172 | +INSTALLED_APPS = [ |
| 173 | + 'django.contrib.admin', |
| 174 | + 'django.contrib.auth', |
| 175 | + 'django.contrib.contenttypes', |
| 176 | + 'django.contrib.sessions', |
| 177 | + 'django.contrib.messages', |
| 178 | + 'django.contrib.staticfiles', |
| 179 | +~~ 'bootstrap4', |
| 180 | +] |
| 181 | +``` |
| 182 | + |
| 183 | +Make sure you change the default `DEBUG` and `SECRET_KEY` |
| 184 | +values in `settings.py` before you deploy any code to production. Secure |
| 185 | +your app properly with the information from the Django |
| 186 | +[production deployment checklist](https://docs.djangoproject.com/en/stable/howto/deployment/checklist/) |
| 187 | +so that you do not add your project to the list of hacked applications |
| 188 | +on the web. |
| 189 | + |
| 190 | +Save and close `settings.py`. |
| 191 | + |
| 192 | +Next change into the `djbootstrap4/bootstrap4` directory. Create |
| 193 | +a new file named `urls.py` to contain routes for the `bootstrap4` app. |
| 194 | + |
| 195 | +Add all of these lines to the empty `djbootstrap4/bootstrap4/urls.py` |
| 196 | +file. |
| 197 | + |
| 198 | +```python |
| 199 | +# djbootstrap4/bootstrap4/urls.py |
| 200 | +from django.conf.urls import url |
| 201 | +from . import views |
| 202 | + |
| 203 | +urlpatterns = [ |
| 204 | + url(r'', views.bootstrap4_index, name="index"), |
| 205 | +] |
| 206 | +``` |
| 207 | + |
| 208 | +Save `djbootstrap4/bootstrap4/urls.py`. Open |
| 209 | +`djbootstrap4/bootstrap4/views.py` to add the |
| 210 | +following two highlighted lines. You can keep the boilerplate comment |
| 211 | +"# Create your views here." or delete like I usually do. |
| 212 | + |
| 213 | +``` |
| 214 | +# djbootstrap4/bootstrap4/views.py |
| 215 | +from django.shortcuts import render |
| 216 | +
|
| 217 | +
|
| 218 | +~~def bootstrap4_index(request): |
| 219 | +~~ return render(request, 'index.html', {}) |
| 220 | +``` |
| 221 | + |
| 222 | + |
| 223 | +Next, create a directory for your template files named `templates` under |
| 224 | +the `djmaps/maps` app directory. |
| 225 | + |
| 226 | +``` |
| 227 | +mkdir templates |
| 228 | +``` |
| 229 | + |
| 230 | +Create a new file named `index.html` within |
| 231 | +`djbootstrap4/bootstrap4/templates` that contains the |
| 232 | +following [Django template language](/django-templates.html) markup. |
| 233 | + |
| 234 | +``` |
| 235 | +<!DOCTYPE html> |
| 236 | +<html> |
| 237 | + <head> |
| 238 | + <title>First step for bootstrap4</title> |
| 239 | + </head> |
| 240 | + <body> |
| 241 | + <h1>Hello, world!</h1> |
| 242 | + </body> |
| 243 | +</html> |
| 244 | +``` |
| 245 | + |
| 246 | +We can test out this static page to make sure all of our code is |
| 247 | +correct before we start adding the meat of the functionality to |
| 248 | +the project. Change into the base directory of your Django project |
| 249 | +where the `manage.py` file is located. Execute the development |
| 250 | +server with the following command: |
| 251 | + |
| 252 | +```bash |
| 253 | +python manage.py runserver |
| 254 | +``` |
| 255 | + |
| 256 | +The Django development server should start up with no issues other than |
| 257 | +an unapplied migrations warning. |
| 258 | + |
| 259 | +``` |
| 260 | +Watching for file changes with StatReloader |
| 261 | +Performing system checks... |
| 262 | +
|
| 263 | +System check identified no issues (0 silenced). |
| 264 | +
|
| 265 | +You have 17 unapplied migration(s). Your project may not work properly until you apply the migrations for app(s): admin, auth, contenttypes, sessions. |
| 266 | +Run 'python manage.py migrate' to apply them. |
| 267 | +
|
| 268 | +July 05, 2020 - 10:59:58 |
| 269 | +Django version 3.0.8, using settings 'djbootstrap4.settings' |
| 270 | +Starting development server at http://127.0.0.1:8000/ |
| 271 | +Quit the server with CONTROL-C. |
| 272 | +``` |
| 273 | + |
| 274 | +Open a web browser and go to "http://localhost:8000". |
| 275 | + |
| 276 | +<img src="/img/visuals/first-step.jpg" width="100%" class="shot rnd outl" alt="Plain old HTML page saying 'Hello, world!'."> |
| 277 | + |
| 278 | +With our base application working, we can now add Bootstrap. |
| 279 | + |
| 280 | + |
| 281 | +## Integrating Bootstrap |
| 282 | +Time to add Bootstrap into the template so we can use its styling. |
| 283 | + |
| 284 | +Open `djbootstrap4/bootstrap4/templates/index.html` back up and |
| 285 | +add or modify the following highlighted lines, which are very |
| 286 | +similar to what you will find in the |
| 287 | +[Bootstrap introduction guide](https://getbootstrap.com/docs/4.5/getting-started/introduction/): |
| 288 | + |
| 289 | +``` |
| 290 | +<!DOCTYPE html> |
| 291 | +<html lang="en"> |
| 292 | + <head> |
| 293 | +~~ <meta charset="utf-8"> |
| 294 | +~~ <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> |
| 295 | +~~ <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css" integrity="sha384-9aIt2nRpC12Uk9gS9baDl411NQApFmC26EwAOH8WgZl5MYYxFfc+NcPb1dKGj7Sk" crossorigin="anonymous"> |
| 296 | +~~ <title>bootstrap4</title> |
| 297 | + </head> |
| 298 | + <body> |
| 299 | + <h1>Hello, world!</h1> |
| 300 | +
|
| 301 | +~~ <!-- Optional JavaScript --> |
| 302 | +~~ <!-- jQuery first, then Popper.js, then Bootstrap JS --> |
| 303 | +~~ <script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script> |
| 304 | +~~ <script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script> |
| 305 | +~~ <script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script> |
| 306 | + </body> |
| 307 | +</html> |
| 308 | +``` |
| 309 | + |
| 310 | +The above new lines in the `<head>` section add a couple of meta elements |
| 311 | +that are important to Bootstrap's styling, and add the mandatory Bootstrap |
| 312 | +stylesheet. |
| 313 | + |
| 314 | +We keep the same `<h1>` header, which will automatically get the CSS |
| 315 | +styling. Then there are 3 *optional* script elements that pull in |
| 316 | +Bootstrap [JavaScript](/javascript.html) for more advanced features. |
| 317 | +We are not using them in this tutorial because we just wanted to |
| 318 | +quickly show how to use the CDN and with this in place you can see |
| 319 | +in the |
| 320 | +[Bootstrap content docs](https://getbootstrap.com/docs/4.5/content/reboot/) |
| 321 | +what you want to add to the template next. |
| 322 | + |
| 323 | +Refresh the page at "http://localhost:8000" and you should see "Hello, world!" |
| 324 | +change fonts. |
| 325 | + |
| 326 | + |
| 327 | +<img src="/img/200705-bootstrap-4-django-template/hello-world-bootstrap-style.jpg" width="100%" class="shot rnd outl" alt="Bootstrap-enhanced HTML page saying 'Hello, world!'."> |
| 328 | + |
| 329 | +If you see that, it means everything works as expected. |
| 330 | + |
| 331 | + |
| 332 | +## What now? |
| 333 | +We just added Bootstrap via the CDN so we can use it in our Django template. |
| 334 | +This was the absolute simplest way to add Bootstrap to a single Django |
| 335 | +page and now there's a ton more you can do with it. |
| 336 | + |
| 337 | +Next, try out some of these other related [Django](/django.html) tutorials: |
| 338 | + |
| 339 | +* [More Bootstrap resources](/bootstrap-css.html) |
| 340 | +* [How to Add Maps to Django Web App Projects with Mapbox](/blog/maps-django-web-applications-projects-mapbox.html) |
| 341 | +* [Monitoring Django Projects with Rollbar](/blog/monitor-django-projects-web-apps-rollbar.html) |
| 342 | + |
| 343 | +Questions? Contact me via Twitter |
| 344 | +[@fullstackpython](https://twitter.com/fullstackpython) |
| 345 | +or [@mattmakai](https://twitter.com/mattmakai). I am also on GitHub with |
| 346 | +the username [mattmakai](https://github.com/mattmakai). |
| 347 | +If you see an issue or error in this tutorial, please |
| 348 | +[fork the source repository on GitHub](https://github.com/mattmakai/fullstackpython/blob/master/content/posts/200705-bootstrap-4-django-template.markdown) |
| 349 | +and submit a pull request with the fix. |
| 350 | + |
0 commit comments