While trying to recall information or acquire a new language, flashcards may be an invaluable resource.
On one side of the card, you pose a question, and on the other, you provide the solution.
The flashcards may be used as a memory aid thereafter.
The longer you look at a card, the more likely you are to remember what’s on it.
Create your own flashcards app using Django.
This guide will show you how to use Django to create a flashcards app that mimics a spaced repetition technique. This project will teach you, step-by-step:
-
Set up a
Django project
-
Work with a
SQLite database
and the
Django shell
-
Create
models
and
class-based views
-
Structure and
nest templates
-
Create
custom template tags
You will get detailed instructions on how to build your flashcards app at every stage.
That’s right; you’ll get all the necessary code sent to you in manageable chunks.
If you follow the link below, you can also get the application’s whole source code:
This project is ideal if you’re looking to expand your knowledge and expertise in Django and Python while exploring a new area of interest.
you!
Demo: Your Django Flashcards App
Create your own flashcard app with this step-by-step guide!
You may see all of your current cards and make new ones on the main page of your web app.
Questions and answers written on flashcards may be revised at any time.
When it’s time to put your knowledge to the test, choose a box and read through the cards within.
The card advances to the next box after the question has been answered.
Just because a flashcard advances to a new box doesn’t imply you’ve finished studying it.
It will continue to move through the boxes when you revisit it at regular intervals to refresh your recollection.
The higher your box score, the more probable it is that you have grasped the principles contained inside.
If you can’t answer a question on a card, it goes back to the beginning of the stack.
box.
Project Overview
This guide will walk you through your task step by step.
This will allow you to work at your own speed, pausing as needed.
You’ll create a database-connected, full-stack web app that mirrors the Leitner system:
While using [the Leitner approach], the student sorts flashcards into categories based on how well they remember each card. Students consult a flashcard in an effort to recollect the correct answer. If they are successful, the card is passed on to the next team. If they are unsuccessful, the task is returned to the original team. ( Original)
While employing spaced repetition, you should check the cards from the other boxes less often but more frequently when learning something new or difficult.
intervals:
- You have five boxes that can contain flashcards.
- When you create a flashcard, you put it into the first box.
- To test your knowledge, you choose a box, pick a random flashcard, and check if you know the answer to the card’s question.
- If you know the answer, then you move the card to the next higher box.
- If you don’t know the answer, then you move the card back to the first box.
The higher the box number, the fewer often you should review the cards housed within. To wit: flashcards are great for picking up a second tongue.
Words from both the English and Spanish dictionaries have been used in the examples throughout this presentation.
The instances, however, are deliberately few and far between.
That way, you may easily modify the questions and answers printed on your playing cards to suit your needs.
You may build upon the solid foundation provided by this project and add more functionality to your Django application. The session concludes with suggestions for projects.
next.
Prerequisites
This project does not need any familiarity with Django or databases. This lesson includes connections to other resources if you’d want to learn more about the subjects covered.
But, you’ll need to be familiar with the command line and grasp the fundamentals of Python and classes in order to get started. Although familiarity with virtual machines and pip is helpful, you’ll pick up the details of getting things set up as you go.
tutorial.
Step 1: Prepare Your Django Project
At this phase, you will set up the environment in which your Django flashcards project will be developed.
Then, you’ll spin up a virtual machine and load it up with everything your project requires.
At this phase, you will start the Django project and double-check its configuration in the
browser.
Create a Virtual Environment
Here, you’ll build the framework for your project. The primary working directory of your project may be called anything you wish. You might call it flashcards app/ and go there, for instance.
folder:
$ mkdir flashcards_app
$ cd flashcards_app
The primary directory of this project will be called flashcards app/.
Any new documents and folders you create will be stored here, or one of its subfolders.
After you’ve found the folder containing your project, you should construct and launch a virtual environment to work in.
By doing so, you confine the installation of any necessary dependencies for your project to its own virtual environment rather than the whole system.
To create a virtual machine on your system, follow the instructions for your operating system below.
environment:
PS> python -m venv venv
PS> .\venv\Scripts\activate
(venv) PS>
$ python3 -m venv venv
$ source venv/bin/activate
(venv) $
Using the venv module, which is pre-installed in Python, the preceding instructions set up and activate a virtual environment with the name venv.
When you see (()) around venv at the prompt, you know the virtual environment has been successfully enabled.
environment.
Add Dependencies
Installing django using pip is the next step after setting up your development environment.
:
(venv) $ python -m pip install django==4.0.4
Your only required direct dependence is the Django web framework.
When you performed pip install, it also installed any additional necessary Python packages for Django to function.
command.
Initiate Your Django Project
Initializing your Django project is the next step after setting up your virtual environment and installing Django.
Create the Django flashcards project at the root of your project using the Django command-line tool.
directory:
(venv) $ django-admin startproject flashcards .
The above command is incomplete; the missing dot (.) must be entered at the end.
Django will not create a subdirectory of your flashcards project if you use the dot.
If you didn’t do this, you’d have a flashcards/ folder within another flashcards/ folder.
By executing the startproject command as seen above, you have instructed Django to create a single flashcards/ folder at the root of your project, including a number of files, including manage.py.
The manage.py file will take over the command line administration.
For instance, it may power your own web server during development.
:
(venv) $ python manage.py runserver
The hostname and port number of your staging server may be entered here.
The server is localhost only and listening on port 8000 by default.
Your Django project may be accessed after the server is up and running by going to http://127.0.0.1:8000 or http://localhost:8000 in your browser.
Your Django project’s landing page.
Only a writhing rocket has been spotted so far.
That means the installation went off without a hitch, and you can keep working on your flashcards.
app.
Step 2: Set Up Your Flashcards App
You have set up your development environment in the previous step.
Prepare your flashcards app, which you’ll call cards, right now.
All the source code for making your learning cards is included in the app.
cards.
Create Your Django Flashcards App
There can be more than one application in a Django project.
A mobile app’s functionality should be restricted.
It may be difficult to tell the difference between a project and an app at first.
This separation of interests, however, is essential for large Django applications to maintain a tidy codebase.
This framework is useful because it allows software to be used in several contexts.
There is a single app you’ll need in addition to your project for this guide.
You may refer to that app as “cards” since its major function is to manage the cards used in your app.
Use the card-making command to make them.
app:
(venv) $ python manage.py startapp cards
Using this command, a cards/ folder containing some sample files will be added to your project.
Add the cards app’s package name to the INSTALLED APPS section of the flashcards/settings.py file to integrate it with the flashcards project.
:
# flashcards/settings.py
# ...
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"cards.apps.CardsConfig",
]
# ...
Your cards app’s configuration class has been imported into the Django flashcard app.
The next step is to check that you can open the cards app on the
browser.
Launch Your Landing Page
Your flashcards website’s landing page on Django still has the animated rocket.
Here, you’ll put up your own landing page by adapting an existing design.
Your cards app will now be responsible for managing the root URL of your project, thus you must first inform Django of this change.
To add your flashcards, open urls.py in the flashcards/folder.
URLs:
1# flashcards/urls.py
2
3from django.contrib import admin
4from django.urls import path, include
5
6urlpatterns = [
7 path("admin/", admin.site.urls),
8 path("", include("cards.urls")),
9]
Line 8 adds a path() call to the urlpatterns array with an empty route string pattern as the first positional parameter.
You tell the flashcards project that the cards app will handle any routes that match the string pattern in the second parameter by passing in include().
Cards now actively monitors all of your root URLs since the empty string pattern (“”) is, in fact, empty.
If you set up your project such that all URLs except admin/ are redirected to your cards app, then Django will handle everything for you.
The urls.py file in your cards app’s root directory will manage this for you.
URLs:
# cards/urls.py
from django.urls import path
from django.views.generic import TemplateView
urlpatterns = [
path(
"",
TemplateView.as_view(template_name="cards/base.html"),
name="home"
),
]
The new urls.py file has the same urlpatterns list as the old one.
Once again, the route string pattern you provide is empty.
This time, though, you’ll redirect the root URL to a TemplateView.
A further optional input is name=”home,” which you also provide.
Views in your Django project are much easier to work with if you give them a name.
This means that you won’t need to modify any templates in the event of a future URL pattern change.
Create base.html in cards/templates/cards/ to host the referenced template.
:
1<!-- cards/templates/cards/base.html -->
2
3<!DOCTYPE html>
4<html lang="en">
5
6<head>
7 <title>Flashcards</title>
8</head>
9
10<body>
11 <header>
12 <h1>Flashcards App</h1>
13 </header>
14 <main>
15 {% block content %}
16 <h2>Welcome to your Flashcards app!</h2>
17 {% endblock content %}
18 </main>
19</body>
20
21</html>
The framework of your website may be found in your base template.
On line 15, you create a re-usable template building piece that may be modified in subsequent templates.
The content of the block will be shown unless it is overridden by a child template. Keep in mind that your Django web server for development should restart automatically. To force the server to shut down, use Ctrl + C in the terminal.
Reboot the server by executing the
following:
(venv) $ python manage.py runserver
Don’t forget that your virtual machine must be running for this command to work.
Go to http://127.0.0.1:8000 in your browser.
Awesome, the card index page is visible in your project.
The template you just made acts as the basis for your introduction.
Your homepage, however, is really boring.
In this article, you’ll find out how to provide your
website.
Sprinkle in Some Style
An aesthetically beautiful layout is crucial to many individuals having a good learning experience.
CSS allows you to apply formatting to your HTML documents.
It is possible to use an external CSS file instead of developing your own.
file:
1<!-- cards/templates/cards/base.html -->
2
3<!DOCTYPE html>
4<html lang="en">
5
6<head>
7 <title>Flashcards</title>
8 <link rel="stylesheet" href="https://cdn.simplecss.org/simple.min.css">
9</head>
10
11<!-- ... -->
You may use the same method as in Python to import a CSS library into your site.
On line 8, you’re bringing in the Simple.css base stylesheet.
You may get style from this external CSS file without having to apply classes to any HTML elements.
Further CSS style allows for even more customization of your CSS code.
Just replicate the underlined CSS code from the expandable:
It is now safe to restart the web server used for development.
Next, go to http://127.0.0.1:8000 to access your flashcard program:
Cool, the flashcard software is starting to seem more professional with the addition of a nice font and centered text.
Knowing CSS will allow you to take your time and make finer aesthetic adjustments to your flashcards app.
When the front end of your site has been polished to your satisfaction, you may go on to the back end.
matters.
Step 3: Reveal Your Cards
Now that your app is live, you may design the tables that will make up your SQLite database.
To do this, a Card model must be added.
In addition, you will make your first set of flashcards in the Django shell and publish them to the
browser.
Connect the SQLite Database
The front of a deck of flashcards is often used to pose a question.
The solution is written on the reverse of the card.
With your card model, you may reproduce the features of a given card.
app:
1# cards/models.py
2
3from django.db import models
4
5NUM_BOXES = 5
6BOXES = range(1, NUM_BOXES + 1)
7
8class Card(models.Model):
9 question = models.CharField(max_length=100)
10 answer = models.CharField(max_length=100)
11 box = models.IntegerField(
12 choices=zip(BOXES, BOXES),
13 default=BOXES[0],
14 )
15 date_created = models.DateTimeField(auto_now_add=True)
16
17 def __str__(self):
18 return self.question
Take note of the NUM BOXES variable on line 5 before delving further into the workings of the Card class.
The quantity of boxes in your program is controlled by the NUM BOXES variable.
You have a good foundation to start using spaced repetition to learn a new language with after completing the first five boxes.
A range from 1 to NUM BOXES + 1, or 6 in all, is stored in the BOXES variable, which is created on line 6.
Instead of the less intuitive zero-based numbering, you may loop through your boxes using the convenient numbers one through five.
Your Card class is described on lines 8–18.
Because your flashcard app will rely on literal translations, you can get away with using models.CharField for both the question (line 9) and answer (line 10) fields.
Even if you wish to remember longer passages, you should be OK with a limit of 100 characters. A longer maximum length may be selected if desired. Keep in mind that the most effective flashcards are succinct and to the point.
Lines 11–14, under “box,” are where you’ll record the box number associated with your card.
The first box is where your flashcard will be created by default.
With choices, you may enforce the restriction that the models.IntegerField value must be an integer between the given values.
The creation date and time of your card will be stored in the models.DateTimeField you add in line 15.
With the date created property, you may sort your card overview such that the most recent cards appear at the top.
At lines 17 and 18, you define. str__() to manage how your Card objects are represented in strings.
You can easily identify the card instance you’re dealing with after you return its question string.
A table in your database to keep track of your cards, as specified by your Card model.
Your database will include fields that correspond to the ones in your Card model.
In order to make changes to your database, you must first construct migrations.
them:
(venv) $ python manage.py makemigrations
Migrations for 'cards':
cards/migrations/0001_initial.py
- Create model Card
(venv) $ python manage.py migrate
Operations to perform:
Apply all migrations: admin, auth, cards, contenttypes, sessions
Running migrations:
Applying cards.0001_initial... OK
...
After making the necessary adjustments to your database, you must next populate it with new information.
You will utilize the Django shell to insert some cards into your database for this purpose.
Get on your terminal and crank up some Django
shell:
(venv) $ python manage.py shell
The Django shell is quite similar to the Python interpreter that you use for interactive programming.
The Django shell, on the other hand, imports all of your Django settings at the outset, so you may begin working in the root folder of a Django project right away.
You may now manage your flashcards project from the shell itself.
Make three of these right now.
cards:
>>>
>>> from cards.models import Card
>>> c1 = Card(question="Hello", answer="Hola")
>>> c1.save()
>>> c2 = Card(question="Please", answer="Por favor")
>>> c2.save()
>>> c3 = Card(question="Sorry", answer="Lo siento", box=2)
>>> c3.save()
>>> Card.objects.all()
<QuerySet [<Card: Hello>, <Card: Please>, <Card: Sorry>]>
You make three cards after importing the Card model.
The Card class is initialized with a question and an answer pair.
The box and date created characteristics on your card will automatically take their default values.
So there’s no need for you to supply them.
Invoking.save() permanently stores the card in the database. Make as many or as few cards as you want. But before you go on in the lesson, make sure you have at least three cards ready to go.
Some of the cards may be moved to new boxes using the box parameter.
This will help you grow your front end to represent a genuine situation, and it will seem like a continuous learning experience.
Cards.objects.all() will return all the cards currently stored in your database.
If you see a QuerySet representation after adding some cards to the database, it means everything went OK.
Putting your cards out in the open is the next step.
end.
List Your First Flashcards
You updated the database with some flashcards in the previous step.
The front end listing of your cards is now open for business.
Create a class-based view that catalogs all of the
cards:
1# cards/views.py
2
3from django.views.generic import (
4 ListView,
5)
6
7from .models import Card
8
9class CardListView(ListView):
10 model = Card
11 queryset = Card.objects.all().order_by("box", "-date_created")
Several standard generic views are available in Django.
At line 9, you build a class-based view that extends Django’s ListView.
With a ListView, you simply need to provide the model to which the view will be bound.
This is accomplished on line 10.
You may refine the results your database provides by specifying a queryset in line 11.
It’s important to collect all cards, but having them sorted by box and created on is also desirable.
This implies that the Django QuerySet you get will have the cards sorted by box and then by creation date.
The “-date created” prefix consists of a dash (-), which places the creation date in descending order.
You must first specify a template before you can see your cards in the browser.
Django has some requirements for the placement and naming of class-based view templates.
A card list.html file located in cards/templates/cards/ is required for your CardListView.
:
1<!-- cards/templates/cards/card_list.html -->
2
3{% extends "cards/base.html" %}
4
5{% block content %}
6 <h2>
7 All Cards
8 </h2>
9 {% for card in card_list %}
10 <h3>card_file_box {{ card.box }} Box</h3>
11 <article>
12 {{ card }}
13 </article>
14 {% endfor %}
15{% endblock content %}
Your card list.html template uses the predefined extends template tag to build upon the foundation of the base.html template.
Keep in mind that the % block content% template element was also included in your base.html template.
From line 6 through line 14, you’re replacing the main body of this code block with your own % block content%>.
You no longer display a “Welcome” screen but rather a list of your flashcards.
Line 9 is a for loop that iteratively processes all of your cards.
Each Card instance’s model fields are available inside the card list loop.
A logical layout for the main screen of a flashcard app would be to display a list of all available flashcards.
Rename the old landing page as card-list and point it to your new CardListView.
:
1# cards/urls.py
2
3from django.urls import path
4# Removed: from django.views.generic import TemplateView
5
6from . import views
7
8urlpatterns = [
9 path(
10 "",
11 views.CardListView.as_view(),
12 name="card-list"
13 ),
14]
Line 4’s TemplateView is no longer required, hence it has been deleted in favor of view imports in line 6.
At line 11, you used to serve a TemplateView, but now you’re serving your CardListView.
You decide to call your new route “card-list” as a result.
Just restart your localhost web server for development, then go to http://127.0.0.1:8000.
Your flashcards are now neatly organized in boxes and timestamped on the app’s main page.
The header and the overall style are carried over from the parent template.
Nevertheless, using ordinal numbers like “1st,” “2nd,” and “3rd” to list the boxes would be more visually appealing and less monotonous.
boxes.
Adjust Your Templates
You currently name your boxes as 1 Box, 2 Box, 3 Box, etc.
This seems to be really complex.
A more user-friendly option is to simply number the boxes, such as “1st Box,” “2nd Box,” and so on.
Django’s humanize template filters allow you to obtain this ordinal numbering.
The information in your template may be made more easily understood with the use of these filters.
Adding django.contrib.humanize to INSTALLED APPS is required for usage.
:
# flashcards/settings.py
# ...
INSTALLED_APPS = [
"django.contrib.admin",
"django.contrib.auth",
"django.contrib.contenttypes",
"django.contrib.sessions",
"django.contrib.messages",
"django.contrib.staticfiles",
"django.contrib.humanize",
"cards.apps.CardsConfig",
]
# ...
Next, in your card list.html, load the humanize template filter after modifying your Django settings.
template:
<!-- cards/templates/cards/card_list.html -->
{% extends "cards/base.html" %}
{% load humanize %}
<!-- ... -->
The ordinal filter will be applied later in your code when you display the box headlines using the humanize filter set.
Keep working from card list.html by inserting an ifchanged template tag around the h3> heading of your new box titles.
once:
1<!-- cards/templates/cards/card_list.html -->
2
3<!-- ... -->
4
5{% for card in card_list %}
6 {% ifchanged %}
7 <h3>card_file_box {{ card.box | ordinal }} Box</h3>
8 {% endifchanged %}
9 <article>
10 {{ card }}
11 </article>
12{% endfor %}
13
14<!-- ... -->
Line 6’s % ifchanged% and Line 8’s % endifchanged% conditionally display Line 7’s box headline only if the value of the headline has changed.
This demonstrates the box number only once, followed by all the cards in the box.
The new headline won’t appear until you scroll to the next box, at which point it will replace the old one.
To use the ordinal filter from the humanize filter set, which is referenced on line 7, you pipe it via the pipe symbol (|).
Similar to tiny functions, filters alter data in-memory before Django displays it.
Here, the ordinal filter is used to transform integers into their textual representations as ordinal numbers.
Your card list.html template may be improved once again.
You are now displaying a representation of your card using the card variable.
As your app develops, you may display your flashcards elsewhere.
Consequently, you should develop your own flashcard template.
In just a second, you’ll have a brand new model of your own.
You should update card list.html so that it displays the forthcoming card.html first.
template:
<!-- cards/templates/cards/card_list.html -->
<!-- ... -->
{% for card in card_list %}
{% ifchanged %}
<h3>card_file_box {{ card.box | ordinal }} Box</h3>
{% endifchanged %}
{% include "cards/card.html" %}
{% endfor %}
<!-- ... -->
The card.html sub-template will now be loaded when the card list.html main template is used.
This form has not been created yet.
Proceed with making the greeting card.html
:
1<!-- cards/templates/cards/card.html -->
2
3<article>
4 <h4>{{ card.question }}</h4>
5 <p>{{ card.answer }}</p>
6</article>
You’ll flesh out card.html with extra information later in the course.
Line 4 displays the query, and line 5 displays the solution, for the time being.
Just restart your localhost web server for development, then go to http://127.0.0.1:8000.
Your boxes’ ordinally numbered headlines display once only.
Your current word translations are shown on the cards below.
Next, you’ll beef up your Django flashcards app so that you can easily create and modify flashcards on the app’s dashboard.
end.
Step 4: Get Into the Card Details
You listed your flashcards in the database after linking your flashcard app to it.
You built your first deck using the Django shell.
Here, you’ll utilize online forms to make and modify your cards at your own pace.
browser.
Create a New Card
If you wish to make more flashcards than you already have, you’ll need to enter the Django shell.
This may be a bit of a nuisance when using the app, particularly if you get an idea for a new card in the middle of a study session.
You can make making a new card easier!
The approach of adding the ability to create a new card in the front end is quite similar to the one you used to add cards to the list.
You start by creating a view, then you make a route, and then you make a template.
Again, the view is implemented using one of Django’s default generic views.
In this case, you’ll be subclassing your CardCreateView with an imported CreateView.
from:
1# cards/views.py
2
3from django.urls import reverse_lazy
4from django.views.generic import (
5 ListView,
6 CreateView,
7)
8
9from .models import Card
10
11class CardListView(ListView):
12 model = Card
13 queryset = Card.objects.all()
14
15class CardCreateView(CreateView):
16 model = Card
17 fields = ["question", "answer", "box"]
18 success_url = reverse_lazy("card-create")
A form with required data will appear on the website where you may make a new card online.
Hence, on line 17 of your CardCreateView, you must not only link the model but also declare the fields that your form should display.
Django will perform a form validation upon form submission and redirect the request to the URL specified in line 18 as the success url.
The similar perspective is being used here.
That way, you can quickly make several cards without having to go between different screens.
Django’s reverse lazy() was imported on line 3 and is used to make direct reference to your card-create route.
There is currently no path to your CardCreateView.
Create away!
it:
# cards/urls.py
# ...
urlpatterns = [
# ...
path(
"new",
views.CardCreateView.as_view(),
name="card-create"
),
]
This new route will let your flashcard app communicate with CardCreateView.
Django now returns the CardCreateView when you go to http://127.0.0.1:8000/new, but it cannot locate a matching template.
Django attempts to load the card form.html template.
As no such sample currently exists, you will need to
it:
1<!-- cards/templates/cards/card_form.html -->
2
3{% extends "cards/base.html" %}
4
5{% block content %}
6 <h2>sparkles Create New Card</h2>
7 <form method="post">
8 {% csrf_token %}
9 {{ form.as_p }}
10 <input type="submit" value="Save Card">
11 </form>
12 <a href="{% url 'card-list' %}">
13 Cancel
14 </a>
15{% endblock %}
Django will display the form fields inside wrapped paragraphs if the code form.as p is present in line 9.
Django forms need the use of the template tag % csrf token% whenever data is submitted.
As a precaution against XSS attacks, this is a security measure.
The ability to halt the generation of a new card is included in lines 12 through 14.
If you change your mind about saving the card, just click the Cancel button and you’ll be sent back to the Website where you keep track of your collected trading cards.
Restart your local development server and go to http://127.0.0.1:8000/new to test out the flashcards app’s frontend with the new card-creation form.
Creating a new card keeps you on the same page, making it easy to make more.
You may return to your card by clicking the Cancel button.
list.
Update an Existing Card
Cool, making new cards really does what you want it to do!
But what if you already have a card and you want to modify it?
Consider for a second what a flashcard’s edit page ought to look like.
You probably envisioned a design similar to your creation page, except that the form would be populated with the card’s customizable content.
Make use of this by creating a CardUpdateView that
similarity:
1# cards/views.py
2
3from django.urls import reverse_lazy
4from django.views.generic import (
5 ListView,
6 CreateView,
7 UpdateView,
8)
9
10from .models import Card
11
12# ...
13
14class CardUpdateView(CardCreateView, UpdateView):
15 success_url = reverse_lazy("card-list")
On line 14, you have a view based on a generic class called CardUpdateView.
This time, though, your view will be derived from Django’s UpdateView (imported on line 7).
Your CardUpdateView is built on more than just UpdateView, however.
To construct a subclass of CardCreateView and UpdateView, you use Python’s support for multiple class inheritance.
By doing so, you may use the best features of both of the parent classes while customizing the ones you don’t need to share.
When you click “Back” after altering a card, you won’t be sent back to the same page you were on during card creation; instead, you’ll be taken to the card list.
Make a new path to the card editor.
page:
# cards/urls.py
# ...
urlpatterns = [
# ...
path(
"edit/<int:pk>",
views.CardUpdateView.as_view(),
name="card-update"
),
]
Make that the path to your CardUpdateView follows the int:pk> convention.
To specify which preexisting card you’re modifying, you’ll need its primary key (pk). Note that Django automatically includes the primary key when a database entry is created, serving as the entry’s unique identity.
You won’t have to keep track of any primary keys for your cards, but you will need this identifier to create a link to the card’s edit page in the future.
If the primary key is an integer and the URL includes the int:pk> component, CardUpdateView will return the information for that card.
You may edit the card with primary key 2 by visiting http://127.0.0.1:8000/edit/2, at which point Django will provide you the view for this card.
In order to support both new card creation and old card modification, card form.html must be modified.
card:
1<!-- templates/cards/card_form.html -->
2
3{% extends "cards/base.html" %}
4
5{% block content %}
6 {% if card %}
7 <h2>pencil Edit Card</h2>
8 {% else %}
9 <h2>sparkles Create New Card</h2>
10 {% endif %}
11 <form method="post">
12 {% csrf_token %}
13 {{ form.as_p }}
14 <input type="submit" value="Save Card">
15 </form>
16 <a href="{% url 'card-list' %}">
17 Cancel
18 </a>
19{% endblock %}
Django’s % if% is used in lines 6–10 to enclose a conditional expression.
When card information is received by your card form.html template, the Edit Card heading appears.
If not, you should show the “Create New Card” button.
When credit card information is available, Django automatically populates your form with the relevant fields.
template.
Connect Your Pages
Now that your new paths are established, you may link your pages together.
Then, in your card list.html, provide a link that takes them to your card-create page.
template:
1<!-- templates/cards/card_list.html -->
2
3{% extends "cards/base.html" %}
4{% load humanize %}
5
6{% block content %}
7 <h2>
8 All Cards
9 </h2>
10 <a href="{% url 'card-create' %}" role="button">
11 sparkles Create New Card
12 </a>
13 {% for card in card_list %}
14 {% ifchanged %}
15 <h3>card_file_box {{ card.box | ordinal }} Box</h3>
16 {% endifchanged %}
17 {% include "cards/card.html" %}
18 {% endfor %}
19{% endblock content %}
Lines 10 through 12 provide a shortcut to the card-create URL, which is useful whenever you want to make a new flashcard in your app.
Display a “Edit” button next to each card’s name.
To add a link to your greeting card, open the card.html template in
bottom:
1<!-- templates/cards/card.html -->
2
3<article>
4 <h4>{{ card.question }}</h4>
5 <p>{{ card.answer }}</p>
6 <hr>
7 <a href="{% url 'card-update' card.id %}" role="button">
8 pencil Edit Card
9 </a>
10</article>
Don’t forget to edit cards/urls.py and add the int:pk> pattern to the route for your card-update URL.
That’s why on line 7 of your % url%> tag you need to include card.id as a parameter.
The ease of switching between pages in your project will increase in significance as it develops.
Adding a menu is a great way to enhance your flashcard software.
To get started, create a navigation.html file that includes a link to your card catalog.
page:
<!-- cards/templates/cards/navigation.html -->
<nav>
<a href="{% url 'card-list' %}">card_index_dividers All Cards</a>
</nav>
At now, your menu options only go to the list of your cards.
In the future, you’ll also add inbound links to the containers that house your flashcards.
You’ve adorned the URL with an emoji once again.
In this case, the emoji for Card Index Dividers () comes before the link for All Cards.
Links and headlines in your app may, of course, include whatever emoji you’d want, or none at all.
Add navigation.html to your main template and it will appear on every page.
pages:
<!-- cards/templates/cards/base.html -->
<!-- ... -->
<header>
<h1>Flashcards App</h1>
{% include "cards/navigation.html" %}
</header>
<!-- ... -->
Now that you know your way around the Django flashcard program, it’s time to start studying!
Please visit http://127.0.0.1:8000 in your web browser.
You may now make changes to existing flashcards in your app.
The next stage is to improve your app to see whether you know the solution to the card’s puzzle.
holds.
Step 5: Check Your Cards
Here, you’ll put in place the logic needed to determine whether or not you already know the solution to a card’s question.
You may advance the card to the next box if you’ve successfully remembered the information. Put the card in the first box if you don’t know the answer.
You’ll utilize the Django shell to test this crucial addition once you’ve implemented it.
That way, you can ensure that your front-end is functioning properly before you ever start creating it.
code.
Make Your Cards Move
Take some time to plan out the spaced repetition algorithm for your flashcard app before you get back into the programming.
If you need a quick reminder on the overall scope of the project, just return there.
A card is advanced one space if the corresponding answer is remembered.
If you are stumped, return the card to the first pile.
Add a.move() function to your Card to simulate the card’s movement between boxes.
model:
1# cards/models.py
2
3# ...
4
5class Card(models.Model):
6
7 # ...
8
9 def move(self, solved):
10 new_box = self.box + 1 if solved else BOXES[0]
11
12 if new_box in BOXES:
13 self.box = new_box
14 self.save()
15
16 return self
You may pass solution into your.move() function.
If you already know the solution, solved will return True; otherwise, it will return False.
The decision of whether to advance the card into the next box or return it to the first box is made at line 10.
If you guessed correctly, the number in your new box variable will increase by one from the one you are using now.
If solved returned False, then new box would be set to BOXES[0], which is 1.
If you knew the solution to a card in your fifth box, new box might possibly be 6.
For this reason, line 14 will only proceed to store the new self.box value if new box is an integer between 1 and 5.
If you know the correct answer to a card in the fifth box, it will remain there.
You’ll put it to the test in the next part.
behavior.
Simulate a Card Check Session
After including.move() in your Card model, test that rearranging the cards still produces the desired results.
Follow the steps in this tutorial that instructed you to use the Django
shell:
(venv) $ python manage.py shell
Using the shell, you may now do check-like interactions with your flashcards project.
To start, bring in your Card.
model:
>>>
>>> from cards.models import Card
After you’ve imported the Card model, you can run a query to get every card from the first and second decks.
boxes:
>>>
>>> box1_cards = Card.objects.filter(box=1)
>>> box1_cards
<QuerySet [<Card: Hello>, <Card: Please>]>
>>> box2_cards = Card.objects.filter(box=2)
>>> box2_cards
<QuerySet [<Card: Sorry>]>
Box1 cards currently holds two cards, whereas box2 cards only has one.
Choose the top card from box1 cards and slide it down one box.
box:
>>>
>>> check_card = box1_cards.first()
>>> check_card.move(solved=True)
<Card: Hello>
>>> box2_cards
<QuerySet [<Card: Hello>, <Card: Sorry>]>
Using check card.move() with solved = Yes causes the card to advance to the next available space.
Your check card has been added to the QuerySet of box2 cards.
To check, try moving the card.
further:
>>>
>>> check_card.move(solved=True)
<Card: Hello>
>>> box2_cards
<QuerySet [<Card: Sorry>]>
>>> check_card.box
3
Like previously, when you solve a check card, you get to go on to the next one.
You still have just one card in box2 cards.
Experiment with not knowing the solution to your card’s puzzle now and see what occurs.
question:
>>>
>>> check_card.move(solved=False)
<Card: Hello>
>>> check_card.box
1
To return the card to box one, use.move() with solved set to False.
Your card’s round-trip functionality is fully operational.
The next phase is displaying your containers publicly.
Next, you’ll add in the ability to see whether you already know the answer to a question and rearrange your cards in the front boxes appropriately.
end.
Step 6: Put Your Cards in Boxes
You checked that the cards were able to be transferred between the boxes in the previous phase.
Here, in the last section of the guide, you’ll finish building out the functionality of your flashcards app.
To begin, make a list of everything you own.
boxes.
Show a Box
Thus far, you have access to sites that allow you to create or edit a flashcard, and a page that displays a list of all your cards.
The ability to choose a learning session’s box is essential for assessing one’s knowledge.
To display a single box, you must have a view for that box, a path to that box, and a template for that view.
To begin, make a view of a single
box:
1# cards/views.py
2
3# ...
4
5class BoxView(CardListView):
6 template_name = "cards/box.html"
7
8 def get_queryset(self):
9 return Card.objects.filter(box=self.kwargs["box_num"])
10
11 def get_context_data(self, **kwargs):
12 context = super().get_context_data(**kwargs)
13 context["box_number"] = self.kwargs["box_num"]
14 return context
It’s CardListView that you’re deriving BoxView from.
By default, Django will use the card list.html template when displaying a CardListView.
Next, create a new file called box.html and edit the template’s template name to link to it.
You also don’t want to show all your cards, which is another way that BoxView differs from CardListView.
You may modify the query set that your BoxView provides using.get queryset(), rather than showing all of your cards.
On line 9, only those cards are returned whose box numbers are identical to the box num value.
In the GET request, the value of box num is a keyword argument.
Templates may make use of the box number by include line 13’s.get context data() call, which adds box num as box number to the view’s context. To distinguish it from the box num keyword parameter, the variable is named box number.
Include the updated path in urls.py so that box num may be part of the URL.
pattern:
# cards/urls.py
# ...
urlpatterns = [
# ...
path(
"box/<int:box_num>",
views.BoxView.as_view(),
name="box"
),
]
Django passes the box number as a keyword argument to your view when you use the int:box num> pattern in your URL.
Yes, that’s what you’re after.
Your view’s box.html template is the next thing you’ll make.
expecting:
1<!-- templates/cards/box.html -->
2
3{% extends "cards/base.html" %}
4{% load humanize %}
5
6{% block content %}
7 <h2>card_file_box {{ box_number | ordinal }} Box</h2>
8 <mark>{{ object_list | length }}</mark> Card{{ object_list | pluralize }} left in box.
9 <hr>
10 {% for card in object_list %}
11 {% include "cards/card.html" %}
12 {% endfor %}
13{% endblock content %}
Again using humanize, the box number in line 7 of your box.html template is displayed as an ordinal number.
The objects that make up your deck are stored in the object list variable. Line 8 utilizes two additional filters on object list.
:
-
With the
length
filter, you show the number of cards that are currently in the box. -
With the
pluralize
filter, you assure the grammatical correctness of your message. If there is only one card left, then the word
Card
stays singular.
But when there are either more or zero cards, then Django pluralizes the word
Card
by adding an
s
at the end.
The box’s cards are displayed in a loop from lines 10 to 12 by including the card.html template.
Simply type your boxes’ URLs into your browser’s address bar to access them immediately.
But it would be much more practical if your flashcards’ menu included links to your boxes.
app.
List Your Boxes
In order to display your boxes, you must first get past a logical roadblock in your app.
The number of boxes in your flashcard app is unknown to the navigation template.
You could add the number of boxes to the context of every view, but doing so would add extra work for no good reason.
Django provides a mechanism for injecting data into templates when writing code.
You can use a special template tag to display a list of your boxes in the main menu.
To be more precise, an inclusion tag will be made.
Return a template with the data provided by a function’s context using custom inclusion tags.
In this case, you’ll send back a dictionary containing the total number of containers.
The inclusion tag is used just like any other loaded tag in Django after it has been created and registered.
Make your desired format or template first.
load:
1<!-- cards/templates/box_links.html -->
2
3{% load humanize %}
4
5{% for box in boxes %}
6 <a href="{% url 'box' box_num=box.number %}">
7 card_file_box {{ box.number | ordinal }} Box <mark>{{ box.card_count }}</mark>
8 </a>
9{% endfor %}
The box links.html template doesn’t deviate visually from the rest of your work.
With the right information, a template can do its job.
The source of the data is irrelevant to the template.
In line 5, you’ll iterate over a dictionary that contains a boxes key in order to satisfy box links.html.
A dictionary can be skipped over using the box links html inclusion tag.
Make a new directory labeled templatetags/ next to your templates/ directory to accomplish this.
Make a new file named cards tags.py under the templatetags/ directory.
folder:
1# cards/templatetags/cards_tags.py
2
3from django import template
4
5from cards.models import BOXES, Card
6
7register = template.Library()
8
9@register.inclusion_tag("cards/box_links.html")
10def boxes_as_links():
11 boxes = []
12 for box_num in BOXES:
13 card_count = Card.objects.filter(box=box_num).count()
14 boxes.append({
15 "number": box_num,
16 "card_count": card_count,
17 })
18
19 return {"boxes": boxes}
How your unique template tag looks like
works:
-
Line 3
imports Django’s
template
module. -
Line 5
imports your
Card
model and the
BOXES
variable. -
Line 7
creates an instance of
Library
used for registering your template tags. -
Line 9
uses the
Library
instance’s
.inclusion_tag()
as a
decorator
. This tells Django that
boxes_as_links
is an inclusion tag. -
Line 12
loops over your
BOXES
. -
Line 13
defines
card_count
to keep track of the number of cards in the current box. -
Lines 14 to 17
append a dictionary with the box number as key and the number of cards in the box as the value to the
boxes
list. -
Line 19
returns a dictionary with your
boxes
data.
Go to navigation.html, where you can include your new template tag after loading it.
it:
1<!-- cards/templates/navigation.html -->
2
3{% load cards_tags %}
4
5<nav>
6 <a href="{% url 'card-list' %}">card_index_dividers All Cards</a>
7 {% boxes_as_links %}
8</nav>
First, you need to load your card tags template tags in line 3.
After being loaded, your custom tags will function just like any other Django tag.
The newly-made boxes as links tag is inserted into the code at line 7. Note: When you add custom template tags to your Django project, you must restart your server manually.
Otherwise you’ll get an error because your custom tag isn’t registered yet.
Visit http://127.0.0.1:8000 after manually restarting your development web server.
Your navigation has access to your boxes and can show the current number of cards in a box regardless of the view you’re currently loading.
Now that your boxes have been added to the menu, you can concentrate on making those individual box pages the best they can be.
themselves.
Pick a Random Card
A new session of card checking should be initiated whenever a box’s page is visited.
So, to put your knowledge to the test right away, your flashcard app should show you a random card.
On your box page right now, you have a list of all your cards.
Improve your BoxView so that a new card is added to your context at random.
:
1# cards/views.py
2
3import random
4
5# ...
6
7class BoxView(CardListView):
8 template_name = "cards/box.html"
9
10 def get_queryset(self):
11 return Card.objects.filter(box=self.kwargs["box_num"])
12
13 def get_context_data(self, **kwargs):
14 context = super().get_context_data(**kwargs)
15 context["box_number"] = self.kwargs["box_num"]
16 if self.object_list:
17 context["check_card"] = random.choice(self.object_list)
18 return context
To create random numbers, line 3 imports the random module.
Keep in mind that a pack might not contain any cards at all.
This is why making sure self.object list isn’t null is essential.
Using random.choice(), select a card from the object list if it contains at least one card.
A learning session in your box.html template will center around this randomly chosen card.
Adjust box.html with the help of check card by adding it to the BoxView context.
:
1<!-- templates/cards/box.html -->
2
3{% extends "cards/base.html" %}
4{% load humanize %}
5
6{% block content %}
7 <h2>card_file_box {{ box_number | ordinal }} Box</h2>
8 <mark>{{ object_list | length }}</mark> Card{{ object_list | pluralize }} left in box.
9 <hr>
10 {% if check_card %}
11 {% include "cards/card.html" with card=check_card %}
12 {% endif %}
13{% endblock content %}
You can now directly refer to check card instead of iterating over each card in object list.
Your BoxView makes use of a conditional statement, so that card.html is only loaded if one is present.
To provide additional information to the included template, you can use with inside the include tag.
You must use the card=check card parameter in the subtemplate because card.html is unaware of the check card variable.
The correct way to determine if you know the answer to a question is to first conceal it.
In addition, the learning session should be the primary focus, so the Edit button should be hidden.
Keep in mind that your card list should be stored in card.html.
It only makes sense to have quick access to the solutions to your problems right there in your card list overview.
Therefore, you must carefully consider how to achieve the goal of concealing the answer on the box page while still displaying it in your overview.
When a template is included, it gains access to all of the variables in the current context dictionary.
Since only your BoxView context has access to check card, you should use it.
Make sure check card is accounted for in card.html.
consideration:
1<!-- templates/cards/card.html -->
2
3<article>
4 <h4>{{ card.question }}</h4>
5 {% if not check_card %}
6 <p>{{ card.answer }}</p>
7 <hr>
8 <a href="{% url 'card-update' card.id %}" role="button">
9 pencil Edit Card
10 </a>
11 {% else %}
12 <details>
13 <summary>Reveal Answer</summary>
14 <p>{{ card.answer }}</p>
15 </details>
16 {% endif %}
17</article>
If check card is true, the Edit button and the response will be concealed.
If the answer is not changed, the card template will display the answer and the Edit button.
To ensure your card.html template is functioning as intended, restart your local web server and navigate to http://127.0.0.1:8000.
Perfect!
Your responses will be displayed in the overview of the card list, along with an Edit button.
When you click on the box on the randomly chosen card, a menu revealing the answer will appear.
In the next part, you’ll learn how to electronically shuffle your deck using online forms.
You should advance the card to the next box if you know the solution.
You have already constructed the backend functionality.
Then, you’ll add the front-end functionality.
end.
Check a Card
When reviewing flashcards, it’s important to indicate to the program whether you already knew the answer or not.
A POST request containing the filled-out form will be sent to the server in this case.
To define your Django form, make a new file and name it forms.py.
:
1# cards/forms.py
2
3from django import forms
4
5class CardCheckForm(forms.Form):
6 card_id = forms.IntegerField(required=True)
7 solved = forms.BooleanField(required=False)
Django models and forms share a similar structure.
Lines 5–7 create the CardCheckForm instance of the forms.Form class.
Your schema for forms has two
fields:
-
card_id
, the primary key of the card you’re checking -
solved
, the Boolean value set to
True
if you know the answer and
False
if you don’t
A required argument can be set on a form field to specify whether or not the field must be filled out for the form to be valid.
At first glance, making both fields required=True seems reasonable.
However, if you are unsure of the correct response, simply leave the forms.BooleanField unchecked.
Therefore, in line 7, you must assign the value False to the required argument. Please take note that the checkbox indicating that you accept the terms and conditions during registration is a typical Boolean field.
Setting required to True would be appropriate here.
Add some code to card.html before you create the front-end form.
:
1<!-- templates/cards/card.html -->
2
3<article>
4 <h4>{{ card.question }}</h4>
5 {% if not check_card %}
6 <p>{{ card.answer }}</p>
7 <hr>
8 <a href="{% url 'card-update' card.id %}" role="button">
9 pencil Edit Card
10 </a>
11 {% else %}
12 <details>
13 <summary>Reveal Answer</summary>
14 <p>{{ card.answer }}</p>
15 </details>
16 <hr>
17 {% include "cards/card_check_form.html" with solved=True %}
18 {% include "cards/card_check_form.html" with solved=False %}
19 {% endif %}
20</article>
You have card check form.html in your code twice.
Form with solved=True argument is included in line 17.
Line 18 does the same thing but with solved=False instead.
Once again, you can pass additional information to the included template by using with inside the include tag.
In this case, you ignore the Boolean that has already been resolved.
Next, your form’s HTML template, card check form, must be created.
Formatting on the form changes depending on the value of a solved
variable:
1<!-- cards/templates/card_check_form.html -->
2
3<form method="post">
4 {% csrf_token %}
5 <input type="hidden" name="card_id" value="{{ card.id }}">
6 <input type="hidden" name="solved" value="{{ solved }}">
7 {% if solved %}
8 <input type="submit" value="check_mark_button I know"/>
9 {% else %}
10 <input type="submit" value="cross_mark I don't know"/>
11 {% endif %}
12</form>
The HTML form is all that’s included in your form template.
Line 4 of the new-card-generation form is where you enter a csrf token.
Your name-attributed CardCheckForm fields are referred to on lines 5 and 6.
Its primary key is stored in the card id field.
The checkbox will be checked or unchecked based on the Boolean value of the solved variable you supply.
Both the card id and solved fields are hidden because they are not required. Your program logic will handle that automatically.
But they’re necessary for the form to be sent through to the back end and processed.
The Submit button, labeled “I know” or “I don’t know,” is displayed in lines 7 through 11.
The only fields on the form that are visible are the two buttons.
Finally, you must modify the BoxView class in order to respond to POST requests.
request:
1# cards/views.py
2
3# ...
4
5from django.shortcuts import get_object_or_404, redirect
6
7# ...
8
9from .forms import CardCheckForm
10
11# ...
12
13class BoxView(CardListView):
14 template_name = "cards/box.html"
15 form_class = CardCheckForm
16
17 def get_queryset(self):
18 return Card.objects.filter(box=self.kwargs["box_num"])
19
20 def get_context_data(self, **kwargs):
21 context = super().get_context_data(**kwargs)
22 context["box_number"] = self.kwargs["box_num"]
23 if self.object_list:
24 context["card"] = random.choice(self.object_list)
25 return context
26
27 def post(self, request, *args, **kwargs):
28 form = self.form_class(request.POST)
29 if form.is_valid():
30 card = get_object_or_404(Card, id=form.cleaned_data["card_id"])
31 card.move(form.cleaned_data["solved"])
32
33 return redirect(request.META.get("HTTP_REFERER"))
On line 15, you should link CardCheckForm to your BoxView using the form class parameter.
Create the.post() function between lines 27 and 33.
This method processes POST requests, as the name implies.
A valid form should be included in the POST request sent to BoxView.
In most cases, the browser will verify that you have completed all mandatory fields before submitting the form.
Still, it’s a good idea to double-check your forms behind the scenes.
The.is valid() function in line 29 accomplishes this.
If the form is correct, then the card id will be used to retrieve the corresponding Card object from the database.
In line 5, you imported get object or 404(), and then in line 30, you used it to retrieve a card or throw an HTTP 404 error.
With a card in hand, you can use the True or False value of solved in a.move() call.
As was previously verified, calling.move() on a correctly answered card advances it to the next level.
If you didn’t already know the solution, then the answer is False.
In that case, return the card to the initial pile.
In the end, you send the request back to the same page you made the initial request from.
The originating URL is recorded in the.META object of your request and is referred to as HTTP REFERER.
Your current check session’s box’s URL can be found in the HTTP REFERER variable.
You can test the procedure by visiting http://127.0.0.1:8000 after restarting your development web server.
Find a container holding cards and quiz yourself:
Your flashcard app will select a card at random from the box you are currently viewing.
You can answer the question for yourself after reading it.
If you agree, then press the corresponding button below.
If you were correct, your card would advance to the next section.
If you didn’t know the solution, your card will go to the first available box.
Good job!
Congratulations, you have just developed your very own Django flashcards app!
Now you can put your card-counting skills to the ultimate test by playing through the deck until the box is empty:
You can customize your study sessions however you like with the flashcards app.
Therefore, you can proceed to the next box once the previous one is empty.
You could ignore the pauses built into your spaced repetition technique or you could give yourself a break.