Welcome to your new Django site!
The site you've just spun up comes with a handful of useful default options that you are free to extend as though it were any other Django project.
For starters, you can play around with the template for the page you're looking at right now,
which is skillitall_mainapp/templates/homepage.html
.
Features
- Starter templates, built with Bootstrap v5.1.3 (served by CDN) and FontAwesome v5.15.4 (served locally).
- The abstract model
core.models.MyBaseModel
and its custom QuerySet and Manager. - django-environ integration to get you started with more secure application settings.
Using MyBaseModel
The "core" app of this project contains an abstract model, core.models.MyBaseModel
.
This can be a good starting point for new models in your project, providing some simple time tracking fields:
If you subclass this model for all models in your new project,
all of your models will share some basic, custom functionality.
For starters, each model includes the time-tracking fields created_at
and updated_at
; as well as a custom QuerySet manager,
core.managers.MyBaseQuerySet
(read more about custom managers
here).
The manager comes equipped with some command-style filter methods for the
created_at
and updated_at
fields. Each of these methods
can be used like any other QuerySet method.
Example:
MyModel.objects.created_before(...).filter(...)
# or
MyModel.objects.filter(...).created_before(...)
Following are the methods currently available in MyBaseQuerySet
:
created_before(dt)
and
updated_before(dt)
Filter instances created/updated before dt
, where dt
is either a datetime.datetime
or datetime.date
object.
created_after(dt)
and
updated_after(dt)
Filter instances created/updated after dt
, where dt
is either a datetime.datetime
or datetime.date
object.
created_on_date
and
updated_on_date
Filter instances created/updated on the date of dt
, where dt
is either a datetime.datetime
or datetime.date
object.
Note that datetime.datetime
instances are detected automatically: the result of their .date()
method is used in the query.
created_between
and
updated_between
Filter instances created/updated within the range of dt1
and dt2
, both of which are either datetime.datetime
or datetime.date
objects.
These methods are simple wrappers for Django's range
field lookup,
which can have unexpected output if you aren't careful.
Please note Django's warnings in their documentation.
Extending MyBaseModel
To add new functionality to all models that subclass MyBaseModel
,
simply add new fields to the model and, optionally, new methods to MyBaseQuerySet
in the same style as those for created_at
and updated_at
.
Whenever you change fields in MyBaseModel
, you should run manage.py makemigrations
and manage.py migrate
for your entire project. Note that this will generate new migrations for every model that subclasses from MyBaseModel
,
so you may have a lot of new migrations to run!
Extending MyBaseQuerySet
on a subclassed model
This one's a bit trickier. When subclassing an abstract model with a custom manager, and adding new manager methods of your own to the child model,
Django recommends (as described in documentation, linked in the warning above) either defining a default_manager
or using a second abstract model as a mixin with extra_manager
.
In this author's opinion, that's more complicated than its worth, as you end up needing to reference different managers for different QuerySet methods; and it does not seem possible to chain simple QuerySet filters between two managers in this way.
Instead, the author recommends the following strategy:
1. Create your new custom QuerySet as a subclass of core.managers.MyBaseQuerySet
:
# my_app/managers.py
from core.managers import MyBaseQuerySet
class MyNewQuerySet(MyBaseQuerySet):
def new_method(self):
...
2. Assign your new QuerySet to the child model as objects
:
# my_app/models.py
from core.models import MyBaseModel
from .managers import MyNewQuerySet
class MyModel(MyBaseModel):
objects = MyNewQuerySet.as_manager()
...
Setting up your new custom QuerySet this way makes every method from MyBaseQuerySet
available to the subclassed QuerySet, neatly ties everything to the objects
default manager,
and allows for QuerySet method chaining:
MyModel.objects.created_before(...).filter(...).new_method()
Templates
This project comes equipped with a basic template you can build on to create your static pages and other views, without needing to see another barebones "Hello World!" page.
For starters, the page you're looking at is the static homepage.html
,
which you'll find in the project's root templates
directory.
It extends from templates/base.html
, where the page layout is defined.
base.html
brings in static files for a local copy of
Bootstrap 5.1.3, including its CSS and bundled JavaScript
(the bundle includes popper.js);
jQuery 3.4.1 loaded remotely; and
a local copy of
FontAwesome 5.15.4 free version
(note that FontAwesome is loaded as a deferred <script>
in the site's <head>
tag,
which loads SVG versions of the icons).
We also load app-specific CSS and JavaScript, which you'll find at static/css/app.css
and static/js/app.js
.
Some CSS is provided to enable to custom footer on this page, while the JavaScript is left empty for now.
"Widget" templates
Some smaller templates are used on the site for the navbar and footer. You'll find them in the project root, at:
templates/widgets/base_navbar.html
templates/widgets/base_footer.html
The only thing making them "widgets" is the fact that they separate some template functionality
into separate files, which are then {% include %}
'ed
in base.html
. Not much more to it than that.
The author mentions these only as a recommendation for further design within your own project, to help you DRY up your code.
Templating the templates
The file templates/template_of_templates.html
(yo dawg)
contains a breakdown for all {% block %}
tags
used in base.html
. When making new templates anywhere in the site,
you can copy this file for a head start.
Simply replace the contents of relevent blocks with your own
and remove the blocks you don't need (but make sure the
{% extends %}
tag is always the first line in the file, per Django requirements).
Next steps
That's entirely up to you! You've got a good starting point here, so get cracking.
If you are truly stuck, consider:
- Django's official tutorial. You can pick up from the Creating the Polls app section and include it in this site for practice.
- Asking a question on r/djangolearning subreddit, or on r/django itself.
- Python Discord's Help channels, where folks can volunteer to provide live support.