I’ve been revisiting A Complete Beginner’s Guide to Django, which goes through the Django library, a Python tool for developing websites. I had tried it a year or two ago, but it just didn’t click. This time around I’ve found greater success precisely because many of the code examples are outdated and will not run without modification. While I may have more to say about using Django some other time, this experience has instead made me consider the efficacy of teaching using poor or broken examples.
I was moving through the tutorial pretty smoothly when I suddenly hit a snag during a test case. The goal of this tutorial is to build a basic “message board” site, where someone can create an account, form new boards, create topics in said boards, then post on the topics.
One of the first things we do is create “models”, which are classes describing each of those components. For example, consider the Topic
class, which should have properties of subject
, last_updated
, board
(which is the board that the topic belongs to), and starter
(which is the user who created the topic.)
The setup for this code provided in the tutorial looked as follows:
class Topic(models.Model): subject = models.CharField(max_length=255) last_updated = models.DateTimeField(auto_now_add=True) board = models.ForeignKey(Board, related_name='topics') starter = models.ForeignKey(User, related_name='topics')
However, when I try to test the setup, I would get an error similar to the following:
TypeError: __init__() missing 1 required positional argument: 'on_delete'
After looking into it, I learned that the version of Django (1.x) that this tutorial was based on made certain assumptions about what would happen of a parent item, such as a board, got deleted. What should happen to the topic in the database? Should it also be deleted? However, newer versions (I’m using 3.x) make no assumptions, and provide several choices for how to handle the deletion. In turns that “cascade” deletion, where if a parent gets deleted then all of the items it keeps track of get deleted, is the correct choice. So, updating the code to add the on_delete
argument to both board
and starter
gets the job done:
class Topic(models.Model): subject = models.CharField(max_length=255) last_updated = models.DateTimeField(auto_now_add=True) board = models.ForeignKey(Board, on_delete=models.CASCADE, related_name='topics') starter = models.ForeignKey(User, on_delete=models.CASCADE, related_name='topics')
Several more of these issues have cropped up. Import statements have changed, and instead of using a function url()
to define new page links, which requires the use of regex, you can use a simpler path()
function. Compare these two equivalent items:
## url() function to create a URL like website.com/board/1/ to link to board with index 1 url(r'^boards/(?P<pk>\d+)/$', views.board_topics, name='board_topics') ## path() function to do the same path('board/<int:pk>/', views.board_topics, name='board_topics')
The list of changes I’ve needed to make goes on. But I’ve found this more useful than if everything worked out the first time. I enjoy the process of debugging. It gets me involved with the code, invested in its success. It forces me to dive into the documentation earlier than I otherwise would when following a third-party tutorial. I learn more about the inner-workings of the tools I’m using, and get a stronger positive feedback loop. I could always copy-paste the final product from the creator’s GitHub, but then I’ve learned nothing.
While the author of this particular tutorial just hasn’t updated it in over 3 years, I think there’s a lot of space for new tutorials to take an error-driven approach. Give the reader some concrete information, but warn them you’re either given a less-than-optimal approach, an old approach, or just introduced a silly error by forgetting an argument. Then, have them use a combination of documentation and built-in error messages to debug.
This may not be appropriate for the brand-new learner, but as someone who has the basics down and is at the point where I’m just learning new information, I want even more of this from the tutorial I’ve been working through. The interactions between views, urls, and models is fairly complex and still feels like a pretty large black box to me. Being forced to do a little bit more work to explore the capabilities of Django would be excellent.
This idea extends beyond learning programming tools. It’s very effective to ask math students to find the error in an explanation, or to critique an approach that may be longer than necessary. Peer review is also an invaluable tool for both parties in many subjects. Taking an honest look at someone’s work can give you more information on how to improve your own efforts.
My final word on this. I made a short post about this idea on reddit, and one comment mentioned how the ultimate version of this is taking an entire tutorial and writing it in another language. I’m certainly not there yet, but I think it’s a good mindset to have particularly with programming languages, where if you have the patience and perseverance you can do anything that’s doable in any other language.