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

If you find this template useful, consider leaving a tip! 🙏

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 is the author's opinion on a best practice, so please take the following as a recommendation only. Refer to Custom managers and model inheritance documentation for more details.

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: