Deploy Your App: Announcing the Talk Python in Production book.

10 tips every Django developer should know

Episode #277, published Mon, Aug 10, 2020, recorded Sun, Jul 19, 2020

We recently covered 10 tips that every Flask developer should know. But we left out a pretty big group in the Python web space: Django developers! And this one is for you. I invited Bob Belderbos, who's been running his SaaS business on Python and Django for several years now, to share his tips and tricks.

The 10 tips

  1. Django Admin
  2. ORM magic
  3. Models
  4. Debugging/Performance Toolbar
  5. Extending the User model
  6. Class based views (CBVs)
  7. manage.py
  8. Write your own middleware
  9. Config variable management with python-decouple and dj-database-url
  10. Built-in template tags and filters

Episode Deep Dive

Guest Introduction and Background

Bob Belderbos is a Python developer and co-founder of PyBites. He has several years of experience running a SaaS platform built with Python and Django. Bob has a passion for helping people learn Python through code challenges and practical projects. In this episode, Bob shares ten essential tips for Django developers, drawn from his real-world experience building and maintaining production Django applications.

What to Know If You're New to Python

If you're beginning with Python and want to follow along:

  • Make sure you understand basic concepts like virtual environments, package installation with pip, and Python modules.
  • Know that Django is a web framework using the MVC (“model-view-controller”) design pattern, though Django calls its components models, templates, and views.
  • You’ll be working with an ORM (Object Relational Mapper) to simplify database interactions.
  • If you need a beginner-friendly introduction to Python, check out Python for Absolute Beginners: A comprehensive course to get you coding quickly.

Key Points and Takeaways

  1. The Power of Django’s Batteries-Included Approach Django comes with built-in tools such as the admin interface, authentication, and an ORM. These pre-integrated features help developers move quickly, especially during the initial project phase. Django’s “convention over configuration” mindset means the defaults can get you very far without deep custom configuration.
  2. Leveraging the Django Admin The admin interface allows you to create a fully functional CRUD backend in just a few lines of code. You can customize admin classes to add search fields, filters, and even prefetch data to avoid the N+1 query problem. It’s primarily for internal use, but it can greatly accelerate debugging and data manipulation during development.
  3. Understanding Django’s ORM Django’s ORM provides a powerful abstraction over SQL. It offers querying methods like filter(), exclude(), or chaining lookups with __ underscores to join multiple related models. Features like select_related and prefetch_related can prevent performance bottlenecks caused by repetitive queries.
  4. Structuring Models for Success Models are at the core of most Django applications. Features like auto_now_add for created dates or auto_now for modified dates reduce boilerplate. Pay attention to relationships via ForeignKey and handle deletions carefully with on_delete=models.CASCADE or alternatives like SET_NULL for data retention.
  5. Extending the User Model Django’s default user model may not have all the fields you need. You can extend it through one-to-one profile models or create an entirely custom user model from the start. If you do create a profile model, signals can automatically generate a profile record after a new user registers.
  6. Debugging Toolbar for Performance Insight The Django Debug Toolbar helps identify excessive queries, slow pages, and potential ORM misuse. It displays detailed SQL logs, timing, and caching info. Just remember to disable it for production since it can expose sensitive details.
  7. Class-Based Views Instead of writing separate functions for handling GET, POST, etc., class-based views let you inherit built-in Django classes like ListView or DetailView. They reduce boilerplate by bundling common CRUD patterns. Knowing when to use function-based vs. class-based views often comes down to preference and complexity.
  8. manage.py Essentials Django’s command-line utility, manage.py, helps with tasks like migrations, testing, and creating superusers. You can also write custom commands for cron-like scheduled jobs via management/commands folder. This keeps operational scripts consistent and integrated with Django’s ecosystem.
  9. Middleware for Global Interception Middleware intercepts requests/responses, allowing you to enforce cross-cutting concerns like authentication checks or GDPR compliance pages. Because it sits before and after the main view logic, you can apply app-wide changes in a single place rather than sprinkling them across multiple views.
  10. Managing Secrets and Environment Variables Never commit secrets or API keys in your code repository. Tools like python-decouple or django-environ centralize sensitive data in .env files. This practice makes deployment safer and easier, especially across multiple environments (dev, staging, production).
  1. Template Tags, Filters, and Built-In Helpers Django’s template language allows custom tags and filters for logic in HTML. Built-ins like |timeuntil or |timesince convert dates into humanized strings, and for ... empty blocks handle empty lists gracefully. This keeps presentation clean and code consistent.
  1. Bonus Tools and Tips
  • Django Extensions: Offers handy commands like graph_models to visualize your model relationships.
  • Celery: Offload time-consuming tasks from the main request loop to run asynchronously.
  • Additional Tools: pgcli or GUI tools like Beekeeper Studio can make database interactions more productive.
  • Allauth / TinyMCE: Quickly implement third-party authentication flows or WYSIWYG fields in your admin or user-facing forms.

Interesting Quotes and Stories

"It took three and a half years of daily work to get to the point where I could leave my day job for PyBites." , Bob Belderbos

"If a user wants to leave the platform, we have to delete all their data, so we rely on Django’s on_delete cascade to clean up all related records." , Bob Belderbos

"One day I emailed half my customers by accident because I tried to do it all inside a request. That’s why we need Celery for big tasks." , Michael Kennedy

Key Definitions and Terms

  • ORM (Object Relational Mapper): A layer that translates between Python objects and database records without writing raw SQL.
  • N+1 Query Problem: Occurs when each item in a list triggers an additional query, often solved by select_related() or prefetch_related().
  • Middleware: Components that wrap around the request/response cycle, applying cross-cutting logic like authorization or logging.
  • manage.py: Django’s command-line tool for running the development server, migrations, tests, and custom commands.
  • Migrations: The process and files that track and apply changes to the database schema over time.

Learning Resources

  • Django: Getting Started: A comprehensive introduction to building web apps with Django, from basic setup to templating, ORM usage, and more.
  • Python for Absolute Beginners: Perfect if you need a foundational Python course before diving into frameworks like Django.
  • HTMX + Django: Learn how to build interactive Django apps without heavy JavaScript frameworks.

Overall Takeaway

This episode highlights Django’s out-of-the-box features and best practices that can supercharge your web projects. From leveraging the admin interface for quick back-end CRUD to scaling with Celery for background jobs and using debugging tools to pinpoint performance pitfalls, these tips help you write cleaner, more efficient Django applications. Django’s “batteries included” philosophy removes much of the complexity of modern web apps, allowing developers to focus on building great features rather than reinventing the wheel.

Bob on Twitter: @bbelderbos
Code Challenges Platform: codechalleng.es
PyBites: pybit.es

Django admin: docs.djangoproject.com
Django admin cookbook: books.agiliq.com
Use some Django ORM magic to get the most common first names: twitter.com/pybites
Django custom manager: riptutorial.com
Debug toolbar: django-debug-toolbar.readthedocs.io
select_related: docs.djangoproject.com
Extending the user model / working with signals / @receiver: simpleisbetterthancomplex.com
Class-based views: docs.djangoproject.com
Comparing class and function-based views: github.com/talkpython/100daysofweb
Example of class-based views: github.com/talkpython/100daysofweb
Django command template: gist.github.com
Django middleware example: gist.github.com

Config settings management:
python-decouple: pypi.org
dj-database-url: pypi.org

Useful template tags and filters: docs.djangoproject.com

for-empty: gist.github.com
is_new filter example: gist.github.com
Asynchronous Tasks with Django and Celery: testdriven.io
Celery debugging - CELERY_ALWAYS_EAGER: twitter.com/pybites
secure.py: github.com/TypeError/secure.py
django-tinymce: github.com/aljosa

Extra tools Michael mentioned
BeeKeeper Studio: beekeeperstudio.io
SimpleMDE: simplemde.com
Human time to Python parse string site (the one I forgot): pystrftime.com
Episode transcripts: talkpython.fm

--- Stay in touch with us ---
Subscribe to Talk Python on YouTube: youtube.com
Talk Python on Bluesky: @talkpython.fm at bsky.app
Talk Python on Mastodon: talkpython
Michael on Bluesky: @mkennedy.codes at bsky.app
Michael on Mastodon: mkennedy

Episode Transcript

Collapse transcript

00:00 We recently covered 10 tips that every Flask developer should know, but we left down a pretty big group in the Python web space there,

00:06 Django developers, and this one's for you.

00:09 So I've invited Bob Belderbos, who's been running his SaaS business on Python and Django for several

00:14 years now, to share his tips and tricks for working with Django. This is Talk Python to Me,

00:19 episode 277, recorded July 19th, 2020.

00:23 Welcome to Talk Python to Me, a weekly podcast on Python, the language, the libraries, the ecosystem,

00:42 and the personalities. This is your host, Michael Kennedy. Follow me on Twitter, where I'm @mkennedy.

00:47 Keep up with the show and listen to past episodes at talkpython.fm, and follow the show on Twitter via at Talk Python.

00:53 This episode is brought to you by Linode and us.

00:57 Have you heard about the 100 Days of Code Challenge? It's a challenge where you write code for an hour a day

01:03 for 100 days. It's helped many developers finally master programming, but it can be hard to know what

01:10 to study or have resources to focus on. That's why we wrote not one, but two 100 Days of Code courses.

01:17 100 Days of Code in Python, which covers mostly pure Python, and 100 Days of Web,

01:23 in Python, which covers a whole spectrum of web frameworks and concepts.

01:27 If you've been thinking about taking the 100 Days of Code Challenge, be sure to visit training.talkpython.fm

01:33 and check out our courses. They are the 100 Days of Projects and Lessons, with a tidy bow on top,

01:38 just for you.

01:40 Bob, welcome back to Talk Python to Me.

01:42 Thank you, Mike. It was nice to be here.

01:43 Hey, man. It's good to have you back. Good to be catching up with you. You know what we should be

01:48 doing is we should be catching up in Pittsburgh and having a beer, but instead we're meeting on

01:52 the podcast on Skype, and that's second best.

01:54 Yeah. There was no Python this year. Rub it in. Rub it in.

01:58 It was... I know. I know. You and Julian both had flights from quite far away to come hang out there,

02:04 and it didn't happen, did it?

02:05 It didn't happen. It will happen next year.

02:07 It will. It's hard to see it now, but life will go back to normal, mostly. I think eventually we'll

02:13 get this whole deal figured out, but right now it's a bit chaotic. So let's just maybe check in.

02:20 What have you been up to since the last time you were on? We had you on two episodes before. You were

02:24 on 140, which was a big one where we kind of met each other and whatnot, talking about 100 Days of

02:30 Code projects. Ultimately, we went on to write a couple of courses, the 200 Days of Code for Python

02:36 and Web Python stuff together. And you were also on episode 224, 12 Lessons from 100 Days of Web,

02:42 which sort of ties into that as well. So it seems like you're on with the tips here. That's a good

02:49 set of topics.

02:50 Yeah, a lot of Django, always on the platform.

02:53 So what have you been up to since back then? That was back in 2019, about a year ago.

03:00 2019. Okay. So yeah, obviously the platform is still growing and that's all on Django.

03:07 Not everyone knows what the platform is, right? So tell people about your Code Challenges platform

03:11 and what's going on there because this is a place that you've been running. How long you've been

03:15 running Code Challenges for?

03:16 Since the end of 2017.

03:17 Right. So three years of running production level Django stuff, running it on Heroku and doing other

03:24 interesting things. So a lot of the tips are sort of pulled out of that experience, right?

03:28 Yes. So it's under CodeChallenge.es and it's a platform where people can learn Python through

03:35 practical exercises, which we have almost 290 now. And yeah, that's pretty exciting. And a lot of,

03:43 learned a lot of Django there. And what's going on for the rest? Well, as you really know, I was a

03:51 Python developer at Oracle until next week, actually.

03:55 Next week, which is actually three weeks ago due to time shifting because we speak in the future or

04:01 the past or something like that. But yeah, so from the time of recording, you're just about to wrap up

04:06 your time at Oracle. Yeah.

04:07 Yeah. And going full into PyBytes, which is pretty exciting.

04:12 So congratulations. Congratulations. That's so awesome. So you basically,

04:17 I think some people would consider it a dream, right? You've made it to the point where you built

04:20 something, curated it over time, and now it's strong enough to be your full-time job, which is

04:26 fantastic. Yeah. So it took three and a half years.

04:28 Is that part of that overnight success that just took 10 years type of thing?

04:33 Yeah. Well, I know that's daily work, right? It's a lot of consistency, but yes, I'm going full on

04:40 that right now. Awesome. So congratulations. And thank you.

04:43 That just means you're focused more on making your platform run well. And obviously,

04:48 Groton is a business. The secret that a lot of people don't learn until they try

04:52 is that running a successful software business is like 30% technology.

04:57 Yeah.

04:58 Yeah.

04:59 So there's a lot more to it, but you still get to focus on your Django side and make the

05:02 platform even better. So that'll be a lot of fun.

05:04 Yeah. It's a platform and we do coaching. So we also help a lot of clients with their Django.

05:09 So, and that's, that's a really nice segue. Yeah.

05:13 Yeah. Awesome. All right. Well, I'm looking at the notes that we put together and, there's

05:18 a lot of tips that we got to go through. So I feel like we trimmed it down. So we're going

05:24 to see if we keep it to 10 plus some unspecified number of bonus items as well. So when I think

05:31 of Django, this first tip that you have, this is like one of the main things that I think of

05:36 is like, it comes with these building block pieces that say flask and pyramid and fast

05:41 API and whatnot, just don't come with like admin backends. Yeah. That's where we're going

05:46 to start.

05:46 Yes. So Django clearly comes with batteries included. And some people actually don't like that because

05:52 they want to rule their own and they think it's opinionated. I happen to like their preferences.

05:57 And yeah, one of those well-known building blocks is the admin interface. And it's, it's pretty

06:03 impressive with a few lines of code. You, you can just add your model in an admin.py file and

06:08 you have the complete crowd interface, which is probably not for the end user, but as a maintainer

06:14 of the site, it's, it's pretty convenient. And, it's an also very easy to extend it. So for example,

06:21 taking an admin class here, you can define some search fields, which makes those fields searchable.

06:27 You can have even have out of complete fields, which they integrated with JavaScript.

06:32 You can overwrite to get query set. And yeah, it's a nice way of inheriting from the admin.model

06:40 admin, class. And, yeah. Yeah. So it, it comes out of the box. Just, I don't know. I kind of

06:47 think of it as like Google sheets or something for your database, right? Right. Where each table is like a

06:53 tab or a sheet in the workbook or whatever the terminology of Google sheets is, but the tabs at

06:58 the bottom, right? Something to, I mean, not necessarily the same UI, but that idea that you

07:02 have like a grid over top of it. But what you're telling me here is I can go and derive special

07:08 classes that are tied to the various tables for the models more specifically. And it lets you search

07:15 different aspects of it. It lets you cool auto-completion. You can also say like limit how many

07:20 items come back in your search results per page. So you don't get a million or something like that,

07:25 right? Yeah. Or you can do a list filter, for example, and then, certain columns at the

07:30 right side of the page, you can, filter on them by clicking, just with one line of code. So,

07:36 that's, that's really some cool functionality. And I think maybe in the show notes, we can link to a

07:42 Django admin cookbook. I use a lot, which has like 50 or so tips. And I might've mentioned that before,

07:48 actually on Python bytes and, I learned a lot from that book. That's like 50 pages and,

07:54 yeah, very, workable code.

07:56 Super cool. Another thing you can do is you can write a function called get query set and actually

08:02 do things like pre-join or do a joined queries against other things. Cause otherwise you may end up

08:09 with like the N plus one problem of ORMs where do one query, then everything that comes back,

08:14 you have to do more queries per item to try to like fill out the details. Yeah. I will talk about

08:18 that in, in one of the tips. Yeah. Yeah. Awesome. That's super cool. Let's see. What else do you want

08:23 to say about that? you know, I, was it you, I think it might've been you, either you or Dan

08:28 Bader from real Python talking about using this to actually trigger events as you interact with this

08:35 admin. Was that you? Yeah. I think that was me. Yeah. Yeah. Yeah. Yeah. So tell us that's like,

08:39 I think that's pretty related. Tell people about that. That's pretty cool. We probably,

08:42 we spoke about that on Python bytes. Yeah. I think what we did was in one of the admin models,

08:48 so for right, the safe method, and then see if the object was created and only then do a certain

08:55 action, like emailing people, but in hindsight, that's probably better to do with a salary task.

09:00 But yeah, that's an example of how you can override those methods and do certain things. Yeah.

09:06 pretty easily. Yes. Yeah. Very, very cool. All right. number two, ORM magic. Yeah. Well,

09:13 Django comes with its own ORM object relational mapper, and it's kind of a proxy to the database. So

09:22 you don't have to write SQL yourself. Although I do enjoy that. I am such a fan. I'm so, well,

09:28 you worked at Oracle, didn't you? I'm such a fan of ORMs actually, or in the, the NoSQL world,

09:34 maybe ODMs or, or some, you know, like R might not necessarily make sense, but you know, there's so

09:39 many issues you can run into if you don't use parameterized queries, you know, little Bobby

09:44 tables and all those issues. And just like, if you get the raw queries back and you pull different

09:50 values and you don't, sometimes you forget to convert this thing to an integer and it's just a

09:54 string that has an integer in it. That's sort of like, there can be all these little weird edge cases,

09:58 right. Whereas with ORMs, it kind of, it's like a, a layer that just separates like how your data should

10:06 be transformed. And I really love it. I think it's great. I know it doesn't a hundred percent work all

10:10 the time, right? There's places where it doesn't, but that's not as, you know, like most of the time,

10:14 it's a, it's a beautiful thing. It's yeah, it's, it's a nice abstraction and it's way easier to use

10:20 and more elegant. I would say it's, it's how Django has, it's pretty Pythonic and you have those helper

10:27 methods, like for example, get object or 404 and it retrieves the object. And if not, it's

10:34 a 4.4 exception. Yeah, exactly. Or equivalent response, right? So it's, it might be exactly what

10:42 you need and you don't even, it's, it's just one method. It's a very concise, right? So if you just

10:47 directly went to the objects and said, get me something where like the primary key is such

10:51 and such, you would get an exception potentially, but it might say an exception, which is, does not

10:56 exist. The model does not exist. And you don't want to tell the user like what that's going to tell the

11:01 user is 500 server error, right? If you don't stop it. And so this will translate that to basically

11:07 effectively catch that exception and raise the HTTP 404, which is the proper response. Yeah. Yeah.

11:14 That's the method is in the shortcuts. So that's clearly like a shortcut. Yeah. And yeah, there are

11:23 so many things I can highlight a few. For example, when you get an object, you can also just manually

11:28 catch the does not exist exception. Nice one is get or get underscore or underscore create, which if the

11:36 object is in the database, it retrieves it. And if not, it creates it and returns a tuple of the object.

11:43 And then a boolean created. So I use it in a few places to sometimes I just want the object. And if

11:52 it's not there, I want to create it. And you can do that kind of in one line of code. And then

11:56 the second return a boolean, the created, you can then say, well, if the object was created, do one

12:03 thing, or if it was not created, do you another thing? And again, it's a shortcut, right?

12:07 Interesting. It's like a proactive upsert, update or insert. But the other way.

12:12 Yeah, yeah. Yeah, yeah, yeah, yeah. Get it or yeah.

12:17 Yeah, that's really cool. All right. What else is good in here?

12:20 Use filter a lot. You can also use exclude. And then there are those neat, like English like methods like

12:27 first. So for example, model dot objects dot first, or dot last, or even preparing, I read about earliest

12:36 or latest, and those read like English, and they do exactly what you expect them to do, right? So that

12:42 reads like, like, like, like Python. Then with the query, and for example, if you're filtering,

12:48 you can use under double underscore to, for example, you have a field, and then you can do

12:55 underscore underscore contains. Is that like a person, like a like, percent? Like, exactly.

13:00 Exactly. Okay. Yeah, that's the SQL language. Yeah, yeah, yeah. Just to highlight the syntax that

13:06 this with double underscore. And you can even link to other models, if you go through underscore,

13:13 underscore, and then the foreign key fields, and then underscore, underscore the attribute. But of

13:19 course, as we said before, we have to be careful with doing a lot of lot of queries there. But

13:24 sometimes you just need to reach out to linked models, right? Yeah. Then if you want to do like more

13:30 complex queries, you can use the uppercase q, the q objects. So you could do q, if one field is this,

13:39 or q, if another field is that. So that's a way to concatenate certain conditions, right? Because a lot

13:47 of times you'll end up, you can use filter and put multiple parameters, but you're going to end up with

13:51 an and which is exactly that's a good point. Yes. Yeah, you don't always want to like sometimes you do.

13:56 But if you don't, filter is not going to help you. That's a great addition, because exactly if you do

14:01 like filter attribute equals comma, another attribute equals there and and yeah, with a q, you can do an

14:08 or yeah, which sometimes is convenient. Yeah. And then we have annotations. So the group by. So for

14:17 example, I have a Django tip, we might be able to link to it maybe in the show notes, how to get how we got

14:24 the most common user names on the platform, which happened to be David, Daniel and Michael.

14:30 It's just a lot of programmers like that. Yeah, I guess so. Interesting. I feel like we almost are gonna have to

14:36 create some gists of your little code. Oh, yeah. And then like link to them, right? We could do that.

14:41 We can totally do that. Yeah. And also this one, what I really like that how the code is laid out over multiple

14:47 lines, as I guess black would do it. And you're looking at the same code, right? It's pretty elegant, I think.

14:53 Yeah, it's super elegant. Yeah, I really like it. And I feel like I write Django RRM code all the time,

14:57 even though I don't use Django, nor do I use a relational database. That's weird, right? And the

15:02 reason is I use a Mongo engine, which is the ORM ODM for MongoDB. But it's modeled on Django ORM. So like

15:12 it's almost identical, you know, as much as it can be, being non relational. So anyway, yeah, it's a really

15:17 nice API. I do like it. And these are cool. Yeah, well, we'll put the gist of how you got the most common

15:22 usernames out of our first names out of your users. That's cool. Connect to it. Okay, I guess hand in

15:27 glove with ORMs, ODMs is the models that define them, right? Yeah, nice segue, right? The ORM free is the

15:34 model. So let's talk a little bit about the models. A few tricks there picked up. Auto now add and auto

15:41 now, which are keyword arguments to model fields. Is this if like I have a date time, like a created date,

15:49 or a last updated date or something like that? Exactly. Yes. So if you have an added and an edited

15:54 field, then the auto underscore now underscore add would add the date upon creation. So typically the

16:01 edit and the auto underscore now would update every time the row is updated. So that's a nice and easy

16:07 way to keep track of those dates. Like when most of the record created. Yeah, yeah, yeah. That's sweet. I love the

16:13 auto now versus... All right. I love the distinction as well. Because sometimes you want it just to default when it's

16:19 created. Other times like last login or last updated for a CMS page or something that would

16:24 be what you want. Yeah. And you don't have to write any code. Those are just attributes that come with

16:28 that field and easy to use. Then we have null versus blank on a field. And the distinction there is that

16:37 if you say null equals true, then the field can be empty in the database, right? And if you say

16:44 blank equals true, then it can be empty on the form, right? So if you don't say blank, then, and you use a

16:50 Django form, it might still think that the field is required in a database. So null is for the database

16:56 and blank is for the form. That's the easy way to remember it. Okay. Yeah, yeah. Cool.

17:00 Underscore delete is an important one because if you link models together and you say on the lead equals

17:06 models cascade, then it will recursively, when you delete a record, also delete the related records,

17:13 the records in the related or linked tables.

17:16 Right. So for example, maybe a code challenges, there's a user, the user has done a bunch of

17:20 challenges. You've saved their results, maybe some other stuff about when they logged in. If you delete

17:25 the user and you have a foreign key relationship, it's gonna, it might even freak out, right? It might

17:31 say, no, this relationship is required. You can't delete the user because there's entries in the

17:36 challenges results or whatever. And this way it'll just, it'll wipe out that entire tree, like

17:41 following those relationships, right?

17:42 Yeah. Yeah, yeah, exactly. And it just deletes all their stuff, right?

17:45 So when I'm in-

17:46 Cascading deletes have always scared me. I've like almost never turned them on in my ass because I'm

17:50 just like, boy, that could go a long way. If that could take more down with it than I actually

17:54 intended. But you know, it's very valuable and useful and I totally get it, but it's cool.

17:58 If a user wants to leave the platform, right? We, we have to delete all their data. So I,

18:04 I still do it manually. So then in Django admin, delete user, and then it shows you like all the

18:10 objects that's going to be deleted. Are you sure about that? And like, I always look like a second

18:16 time. Is that indeed the case? And, and yes, it all ties back to the user. So you don't want to delete

18:22 the user and have all kinds of ghost objects in the database potentially. So that's, it's actually useful.

18:28 But if you want to keep the records, you can, instead of models cascade, you can use models set

18:34 null and then it keeps the records, but where the foreign key was user, it sets it to null.

18:40 So that's, that's a good alternative.

18:42 So it kind of cascades it. So the relationship's not broken, not in the database is corrupted sense,

18:48 but it just unwires it. Yeah. Yeah. It makes like there was a user link, but now it's none. Like it's anonymous

18:55 now. This portion of Talk Python to Me is brought to you by Linode. Whether you're working on a

19:01 personal project or managing your enterprise's infrastructure, Linode has the pricing support

19:06 and scale that you need to take your project to the next level. With 11 data centers worldwide,

19:11 including their newest data center in Sydney, Australia, enterprise grade hardware, S3 compatible

19:17 storage, and the next generation network, Linode delivers the performance that you expect at a price

19:23 that you don't get started on the node today with a $20 credit and you get access to native SSD storage,

19:29 a 40 gigabit network, industry leading processors, their revamped cloud manager at cloud.linode.com,

19:35 root access to your server, along with their newest API and a Python CLI. Just visit talkpython.fm

19:42 slash Linode when creating a new Linode account, and you'll automatically get $20 credit for your next

19:47 project. Oh, and one last thing. They're hiring. Go to linode.com slash careers to find out more.

19:53 Let them know that we sent you.

19:57 Yeah, my current systems, if somebody deletes their account, we have a deleted users table next to our users table,

20:04 and then we just null out all the stuff that identifies them, but we still have the ID. So if I go back and say,

20:10 this user did this thing, I can still trace back that ID and go, oh, that's actually a user that used to exist who

20:16 deleted themselves. That's interesting. Yeah, that's, that's cool, because you do have then the original

20:21 data. Yeah, yeah, yeah, yeah. But this, I mean, this, if you're happy to make it go away, it's really handy,

20:27 because with relational stuff, it's really hard to like, all right, well, I'm going to traverse all the

20:31 relationships. And how do you unwind them so that the database will actually accept the delete, right?

20:37 So this, this is really nice. Yeah, cool. And one more model managers, right? What's up with that?

20:41 Yeah, there is an example that you can define your own model or query. For example, here I linked it to,

20:49 we can, we can link to it, a forecast day manager, and it has a latest method. And it, you know, it gets

20:58 the latest forecast date or does any query really, and then you can just call that method on the object. So

21:05 model.objects, your method. So it's kind of a nice way to not have all that

21:11 query logic in the view, potentially, but have that in the model where it should be. And then it's like

21:18 a property, right, where you do some, a bunch of complex stuff, and then it's just instance.yourproperty

21:25 name, which is very clean, right? So I thought that was...

21:29 Yeah, let's write it once and use it in other places without thinking about it, right?

21:32 Yeah. And then again, it reads like English, yeah, and abstract that logic in the model.

21:37 Yeah, beautiful. Beautiful.

21:38 Yeah. And meta classes, which are not really meta classes. Well, I guess they are, but you

21:43 can define it.

21:44 Database meta, not Python meta.

21:47 You can make a class meta in your model, and I don't use it for many things, but for example,

21:54 I think most common example is that you can define the ordering there. For example, you can say

22:00 dash edit, and then in your admin, or wherever, whenever you query that model is, then the objects

22:08 show descend in descending order.

22:10 Right. So the default sort, if you don't specify one.

22:12 Yeah.

22:13 Yeah, that's one example.

22:14 Yeah, that's really handy. Nice, nice.

22:16 All right. Now, this, so far, what we've been talking about, besides my little diversion to Mongo

22:21 engine, which is not a very common use case, has been mostly Django specific. But this next one that

22:28 you got here, I feel like a lot of the frameworks have an equivalent, but a lot of people probably

22:32 don't use them regardless of the framework they're in.

22:35 And that's the debugging toolbar, right? So going more into the developer toolset. Yeah, which has been

22:41 very useful. It's a plugin, so you pip install it, you add it to the installed apps, I believe. And I think

22:48 there's a middleware as well. So a couple of two or three settings. And then, of course, you load it only if

22:54 you are developing locally, or which typically means is when your debug is set to true. Not in production.

23:01 That is super, super important. Yeah, there's tools out there that will scan websites for the

23:06 presence of these debug toolbars and try to pull them up. It's bad news to have them out there. So

23:11 yeah, I mean, there's been a lot of vulnerabilities around this, actually.

23:14 And those are important and easy tweaks, maybe a little segue. For example, you have like the default

23:20 dash admin, slash admin, that's super easy to find, right? So it's very easy to in your URLs

23:28 to make it like my dash backend, and then make it a little bit more hidden, right?

23:33 Well, yeah, exactly. I mean, I don't know how many people have like pulled up the logs for their

23:38 website, as in the request logs live and just go to the server and tell your logs or whatever,

23:43 you will see untold number of requests for various PHP pages and node JS pages and whatnot.

23:51 Yeah, it has nothing to do with your site is they just like, all right, here's the five most common

23:54 backends, you know, WP admin dot PHP and whatnot, right? They're just looking for them. So surely

24:01 they're looking for Django admins as well.

24:03 Yeah. So yeah, watch out with a bit of security. But the main reason I pulled up the debugging

24:10 toolbar here was a story performance story. And on some pages, they became pretty slow. And it's like,

24:16 okay, it could be more data, more users. But actually, then when I start looking at those pages,

24:22 how they load it, with the debugging toolbar, I saw that, yeah, there were actually hundreds and

24:27 hundreds of queries happening. And that was imagine that that would be slow. Yeah, the servers on a

24:33 different the database is on a different server, physical server or virtual server, right? It's got

24:39 the network latency and all that. Right. Yeah.

24:42 And it was actually because I was not using select underscore related to link models in an efficient

24:48 way. So when you use select related, it makes one extra query to link up to two models. But then every

24:55 time you access a foreign attribute, so to say, it's already there, it's like a pre cache. So once I got

25:01 that in place, then the page became super fast. And but the main point was that the debugging toolbar

25:08 made that super easy to to diagnose, right? Yeah. And I think probably hearing the word debugging

25:13 toolbar, a lot of people who've not like, you know, like, what is this annoying thing that's on

25:17 the side of my website? I don't make this go away, right? Like, they haven't really explored it.

25:21 It does catch errors. And to me, like, that's the least value that the thing adds, the debugging part is

25:27 completely, I don't know, I have other tools that tell me that you can just look at the output and the

25:31 stack trace. But the real value is in the performance, you can check off things like track the

25:37 performance of the actual profiling level of your code and show me where the time is being spent.

25:43 And then also into the database, right? Like, these are the queries that are happening on this page,

25:48 and how many and if you see like, Oh, I'd expect two or three, and there's 500, you're like, Oh,

25:52 this is really broken. Let's figure out why we're going and getting its second table over and over and

25:58 over again. Yeah, exactly. Yeah, yeah, yeah. So Blask has an equivalent one of these pyramid has an

26:05 equivalent one of these. They're awesome. You should definitely check them out. Don't deploy

26:09 them in production. Yeah.

26:10 Number five.

26:13 Yeah, I had extending the user model. And I linked to a an article simple from simple is better than

26:21 complex, which is a great jangle blog. And it's more about strategy, like when you start a project,

26:29 and you inevitably going to store more data on a user, right? There are preferences, there are profile

26:37 settings. And you have to kind of think like how you, you're going to do it from the start, because

26:43 one way or another has quite an impact on your code. So the article has four methods, proxy model,

26:51 the one to one with a profile table, and extra two forms of extending the abstract user base class.

27:00 And especially that those last two options are really you want to do only from the start, and I believe

27:07 before running the initial migrations, but I would have to look that up. For the platform, we actually

27:13 went with the one to one model, the profile model, which might cause a few queries there, but it has never

27:21 been really a performance issue. Does lead to pretty easy to maintain code. And we use the signal actually, or I think

27:31 they're called signals, which is interesting to highlight that when the user model gets created, you can use the

27:39 receiver decorator, with a post safe, and then write literally three lines of code that the profile object gets

27:47 created once the user object is created. So basically, when when a new user signs up to your Django app, right?

27:54 And so it, if you need one table, the likes you have a preferences table and a users table, whatever you create

28:01 the user, you want to automatically have the preferences created to defaults or whatever. And so you can wire it up,

28:06 basically an event that is triggered through the ORM, that when you first create one, it's going to set this up, and it's going

28:14 to initialize it with one of these, right? Correct. Yeah. And I think it's a nice trick to know about, like how you can

28:21 signal one model to another, and have the other model do something based on the other one. I think that's a useful trick.

28:27 Yeah. Well, I think an overall general principle that's really, I found really super valuable around like these kinds of

28:34 models and database stuff is, as much as possible, use the ORM to do the default thing, like the add,

28:43 add now, or auto now, auto add, auto now add, that kind of stuff. Yes. Right? So when I create the object, if I don't set anything, a whole bunch of stuff will get filled out. And only the things that I have to specify, do you have to actually go right, as opposed to always updating the updated time yourself or something like that, right? So as much as you can lean on those things, and this is like a,

29:04 cross model version of that. Yeah. Yeah. So usually when you want to do something, the ORM has, has a default way. Yes. So again, it comes back to the previous, or tip number three, like know the ORM pretty well, right?

29:18 Yeah, that's for sure. I don't know how you feel, but when I go around the internet, I feel like there's a lot of sites that just don't understand how their web app interacts with their database. You go there and it takes like six seconds for the page to load.

29:32 Or, you know, you go there and there's like four parts of the site that are like spinning little Ajax things for like five seconds. You're like, what is wrong with you? It can't be this, cannot be this slow. Like it just cannot, I don't care how many million records you have, you got to be able to use an index and make this a little bit faster than this. Right. And just, but it's ongoing, I think.

29:50 Oh, that's a good point. The database index is super important. Yeah.

29:54 Yeah. Yeah. And you can specify another attribute on, yeah. Another attribute you can just specify there. Yeah.

30:00 Yeah. Yeah. I think indexes are definitely, indexes are like magic speed dust you can throw in your database. They're beautiful.

30:07 Yeah. Yeah. No, I'm not sure the ratio, but it can, it can be a hundred times faster, right? It's, it's.

30:12 Oh, easy. A hundred times faster. It's, it's insane. Yeah. And it's, it's like one little line of cut, like index equals true type of stuff. Right. So, so simple, but people overlook it. So, yeah.

30:22 Yeah. No, especially in enterprise with enterprise apps, there are always hammering on, on indexes. Yeah.

30:27 That's very important. I can imagine. I can imagine. All right. Now what's number six?

30:33 Class based views.

30:35 Yeah. So with class based views, these are basically one class corresponds to a URL, but that has different functions to respond to the different HTTP verbs, like get, post, put, that kind of stuff. Is that what these are? Are they different?

30:46 Yes. That's a, I would have to look up the exact classes, but you have typically like list view, detail view, which then tie into the crowd operations. Yes. Yes.

30:56 Right. So you can even have one for head, which is not, not that common, but sometimes, you know, if you're looking for like a browser is looking for, should I actually download this thing?

31:05 Tell me the E tag, you know, last modified sort of thing. So I know whether or not my cache is still valid, stuff like that. Yeah. Very cool. Okay.

31:13 So yeah. And, and I'm kind of torn because I'm looking here at, one of the, you know, the apps for the hundred days, of web where we showed function based versus class based.

31:24 And I like them both because function based, you kind of see what's happening that you, you pull all the objects, then you render a template with, give it a dictionary of things you want to render in the template.

31:36 So it's a, to me that already looks pretty Pythonic and compact, but then if you compare the class based, you see all that inheritance happening.

31:46 For example, we have a quote list, which inherits from list view, which then is that magic superclass with all that default behavior, which is cool.

31:54 If you know them very well, but it can also be hiding a lot of stuff away.

31:58 So for example, the, the list view, the only line there is model equals quote, which says saying like, build me a list view around that model.

32:06 And then stuff magically happens.

32:08 Right.

32:08 So it's cool, but it's also hiding a lot of stuff.

32:11 Yeah.

32:12 Yeah.

32:12 I hear exactly what you're saying.

32:13 I feel kind of the same way.

32:15 No, but, it's definitely inheritance and, you can override certain methods.

32:20 So if you know them well, it's, it's, it's pretty elegant and robust.

32:23 Yeah.

32:24 Nice.

32:24 All right.

32:25 Number seven.

32:25 This also feels very much like the batteries included side of the Django story.

32:29 Yeah.

32:30 I call it miners.py and I guess it can be an episode in itself, right?

32:33 Yeah.

32:34 Pretty much.

32:35 Pretty much.

32:36 I just thought of the highlight.

32:37 If you, kind of, how would you say like meta things, how you manage a, well, that's

32:45 why it's called manage.py, how you manage a Django app.

32:48 Important is the migrate.

32:49 Of course.

32:50 We, we all have used it.

32:52 If you start a Django app.

32:53 You run migrate.

32:54 migrating.

32:55 you're going to have a lot of stuff.

32:56 is, you're going to have a lot of models.

32:59 you're going to have a lot of models.

32:59 you're going to have a lot of models.

32:59 you're going to have a lot of models.

33:00 the user authentication stuff, embedded.

33:02 But for example, you can use fake initial on migrate.

33:06 And then if the database is kind of further ahead and there's already stuff there, so it's out

33:11 of sync, then fake initial is a way to kind of tell Django, well, if it's already in the

33:17 database, ignore it, which can be useful.

33:19 If, if your database is out of sync, you have low data.

33:22 For example, if you want to have a bunch of data in your initial database, which sometimes

33:29 is referred to as pictures, and you can give it a JSON file and pre-populate your database,

33:35 which can be useful.

33:36 Then we have shell, which is cool because if you do Python manage.py shell, you drop into

33:42 the Django shell and you can play with the ORM directly.

33:47 So you can do like from models, import bytes, and then it can do byte objects, count,

33:51 byte objects, last, whatever ORM stuff.

33:55 So it's a nice way to...

33:56 It's like a REPL for your web app.

33:58 A REPL for your, for your web app.

34:00 Yeah.

34:00 And you can play with the objects there.

34:02 It's, it's pretty useful.

34:03 Yeah.

34:03 It's really handy because if you work with the ORM, you might have some of these auto, like

34:09 linking auto features that set values.

34:12 And if you flip over raw to the raw database, then all of a sudden you lose all that stuff.

34:17 that you've set up to help you kind of manage and make sure it's all consistent.

34:20 Right.

34:20 This way you can kind of script around in there.

34:23 Yeah.

34:23 But with that safety or whatever you call it.

34:25 Yeah, exactly.

34:25 Yeah.

34:26 And then if you load in your production database, you can actually do that on your live database.

34:31 So that's pretty cool.

34:32 Yeah.

34:32 And useful.

34:33 You want to debug something, manage.py test to run the test.

34:38 Not much more to say about that.

34:40 Then most people that use Django will know.

34:43 Create super user.

34:44 So that, gives you a bunch of, prompts to create a user with all the permissions, which

34:50 is useful to have to quickly log into the backend.

34:53 I, and, and the one I really like, lastly is the Django commands.

34:58 So you can write your own kind of use it for cron jobs.

35:02 For example, you can write a command, which inherits from base command and you, overwrite

35:09 the handle method.

35:10 And then typically what, for example, we use it to, for example, we have people on the

35:16 platform doing trials and the trials is two weeks, right?

35:18 So after the two weeks, they are, they're done with the trial and we have to take a couple

35:23 of settings on their profile to, get rid of that access.

35:27 And so we have a command, that queries, all the, the profiles, profile objects of people

35:34 that have an active trial and then compares, like when they started to trial with daytime

35:39 now or time zone now, and does one action already removes the access.

35:44 And that's a, a script that's under the app management slash commands slash script.

35:51 And then in a Roku, we use scheduler.

35:53 And then we say, well, every day at 9am run that script.

35:56 And if you have a script in managed management slash commands, then managed.py recognizes

36:04 that as, as a command.

36:05 So if you put a script in there, then you can use it through the managed.py interface, which

36:10 is pretty cool.

36:11 So you can write your own cron jobs basically.

36:13 Yeah.

36:13 That makes sense.

36:13 Super cool.

36:14 Yeah.

36:14 Yeah.

36:15 No, that's, that's really nice.

36:16 So it basically lets you just add these commands and then tie it into the Heroku scheduler

36:21 or celery or something like that, that it becomes this cool cron job that knows all about your

36:26 site.

36:26 Yeah.

36:26 So in Heroku scheduler, we would define every day at 9am, run Python, manage.py and then

36:33 name of your command, which is the name of your script.

36:36 And yeah, just like that.

36:38 Nice.

36:39 Yeah.

36:40 We can, we have here on the notes, a template, some template code, how, how that would work.

36:45 Maybe we can make a gist of that as well.

36:47 Yeah.

36:47 I think we have to make some gists.

36:48 Yeah.

36:49 All right.

36:50 Number eight is all about middleware, which is code that runs that might see the request

36:57 before it actually hits your end view methods or action methods or potentially changes them

37:02 after it's already been processed and decided, but before it actually goes out the web server

37:08 to the end user.

37:09 So it's like a before and after view of it, right?

37:13 Yeah, that's not super common, I think, but it's a nice way to have some sort of global

37:18 layer in your pipeline, in the request pipeline of Django and practical example.

37:24 We, how we used it on the platform was to remember when we had to make those GDPR changes, like with

37:32 the regulations and stuff.

37:33 Yeah.

37:33 I know we talked about this and we all had to do weeks of rewriting stuff to make sure,

37:39 even though it was in the same vein, it wasn't technically matching every rule.

37:43 So we had to go and go through all that and make sure there was.

37:46 Yeah.

37:47 It could be another episode, the stories we have from that one.

37:50 But so we made all these changes and we had to have some sort of form with already 10K users

37:57 or more to have them kind of say yes to this at one time.

38:02 They had to have like another acknowledgement the next time they visited the site, regardless

38:06 of where they came into, not through the login flow or whatever.

38:10 Yeah, because they were already a user, right?

38:12 So they went through the registration flow.

38:13 So we couldn't put it just there.

38:15 We also had to do for the existing user.

38:17 So we wrote a middleware that redirected, that kept track if they had their consent, gave their

38:24 consent on their profile.

38:25 And if not, it would redirect them to that page, to that form basically, where they would

38:30 give their consent.

38:31 And yeah, it was a nice way to enforce that login, that sorry, that redirect based on a

38:38 profile setting basically.

38:39 Yeah.

38:40 So middleware was perfect there because it's kind of ruthless as it sits in front of all the

38:46 requests.

38:47 That's a really, really good point that it's ruthless.

38:49 And in a good way, in a sense, right?

38:52 Like if you were to think, okay, well, users need to make sure we got to make sure they accept

38:56 the GDPR at this point before they go on.

38:58 So let's check where they go and perform a byte, where they view their solution, maybe where

39:02 they view their account and where they log in.

39:04 And there might be some other part of the site that you forgot about, right?

39:08 And in addition to just having to modify all those places, it's super easy to forget one

39:13 weird edge case.

39:14 But with middleware, every request that hits Python, that hits Django, hits this thing, right?

39:20 And so there's no escaping it.

39:22 You've got to accept the thing or you're getting redirected.

39:25 Yeah, exactly.

39:26 And yeah, I also linked here to a cool article, again, from Vitor Freitas, where he wrote a bunch

39:33 of middleware to catch for any exception that is handling.

39:37 He goes out to Stack Overflow.

39:38 It's called the Stack Overflow exception troubleshooting.

39:41 And only in debug mode, of course, it would list the top three answers.

39:48 Stack Overflow answer, right?

39:49 Oh, that's incredible.

39:50 Yeah, I saw it yesterday.

39:52 I was like, this is so cool.

39:53 That's crazy.

39:55 Such a cool use case.

39:57 So I just wanted to...

39:58 Yeah, that's a wild use case.

39:59 How interesting.

40:00 I'm glad you're linking to that.

40:01 Yeah, the two places that I use that I'm really actively aware of using middleware,

40:06 my sites.

40:07 And Pyramid has middleware as well in Flask and so on.

40:10 One is for things like Sentry, where it's looking at for any unhandled errors.

40:17 And instead of just letting the server 500 page go back to the user, it'll catch it, report

40:22 it, and then send it on, right?

40:24 So it'll gather up all that information.

40:25 And you basically just register that.

40:28 You don't have to do a lot.

40:29 And the other one is this thing I'll just throw out right through on the bonus round really,

40:32 really quick is a thing called SecurePi, which will add all the headers that you should be

40:37 adding for security.

40:39 Like, do not allow my site to be embedded in an iframe on another site.

40:42 Right?

40:43 And the middleware will say, I don't care how you got to this request, but we're going to

40:47 tell you, you can't embed this, whatever this is, on another site in an iframe.

40:52 Oh, that's cool.

40:53 Right?

40:53 So, yeah, it's super cool.

40:55 And it's like literally three lines of code to do it.

40:57 But because it's middleware, you know you're not forgetting it.

41:00 The whole site just now, all of a sudden, adds the right extra security headers.

41:04 Yeah.

41:04 If you're updating 20 views, then it's time to pause and reflect.

41:09 It's a good time.

41:09 If there's a better way, right?

41:10 Exactly.

41:12 Exactly.

41:12 Yeah, yeah, yeah.

41:13 Cool, cool.

41:14 So speaking of better ways, number nine, you should just put your API keys and your passwords

41:19 right inside the code, right?

41:20 No.

41:20 But not the Vue method, probably.

41:22 No?

41:22 No?

41:23 No?

41:23 Okay.

41:24 You mean like committing your secret key to version control?

41:28 I'm just kidding, yeah.

41:30 I know you're...

41:33 Yeah, it's...

41:34 No, that's an important thing.

41:35 There's stuff that you, of course, should keep out of version control.

41:39 The way to do that is to, for example, have a .env file, which you then ignore in your .gitignore.

41:47 And then those keys are hidden forever, right?

41:51 But you still need to reference in your settings.py.

41:55 And there are two packages that make it pretty friendly and easy.

42:01 First one is Python decouple.

42:03 And you import, for example, from decouple, import config, and maybe CSV, depending on the setting.

42:10 And then you can just do secret key equals config, and then the key, right?

42:15 Secret key, which is the actual secret key line in your .env file.

42:20 So it magically knows that there's an .env file, reads it in, and extracts the key from it.

42:26 So really nice plugin to handle your settings.

42:30 Okay.

42:31 Yeah, this is really cool.

42:32 I hadn't heard of that one.

42:33 That's neat.

42:34 Yeah, I always recommend it to anybody using Django, really, to just pip install Python decouple and make their lives easier.

42:40 Yeah.

42:42 And similarly, for the database, you can use dj-database-url.

42:48 Very similar to decouple that it uses the config method.

42:53 And you can just give it default equals config and the database URL.

42:57 And yeah, so as you see here, we can put the snippet in this show notes.

43:02 The database's dictionary config section is very compact.

43:07 You don't have to...

43:08 Well, obviously, you have to have the right database URL string in your .env file, but it's much easier, it feels.

43:15 Right, right.

43:15 And database strings often contain username, passwords, host names, host ports, all the kind of stuff that you kind of not want to share.

43:22 Yeah.

43:22 No, that needs to be all hidden away.

43:26 Yeah.

43:26 And one tip I do have regarding the .env file.

43:30 So as the .env file is hidden from version control, it won't show up on GitHub.

43:35 So I recommend to commit a .env.example file with the same strings, with the same keys, but of course, so the same, for example, secret key, allowed host, and then, of course, not the actual data.

43:49 But just as a way to communicate to other developers, like this is the .env file we're going to use.

43:54 So copy this to a .env file and put in your credentials there.

44:00 Right.

44:00 But it's a nice way to communicate.

44:02 Type the actual database connection string here, type the actual encryption key here, whatever, AWS key there.

44:08 Right, because the platform has like 20 or 30 of those variables, and if somebody would start developing on that project, how do they know, right, what they said?

44:17 Unless you run server and just start screaming about missing keys.

44:23 Yeah.

44:23 Until it crashes.

44:24 Until it crashes.

44:25 The thing is, though, sometimes those don't crash right away, right?

44:27 Like maybe you have a MailChimp API key, and the only time that's going to scream and crash is when you actually try to register a new user, not log in an existing one, or, you know, something weird like that.

44:39 And then like you push it to production and bad things happen.

44:42 Yeah, it's a silent error.

44:43 That's the worst.

44:44 Yeah.

44:44 Yeah, exactly.

44:45 Yeah, it's better if stuff blows up, then it goes silent.

44:49 Like the center five, right?

44:51 No error should pass silently.

44:53 Yeah.

44:54 That's right.

44:55 That's right.

44:56 So, yeah, it's a big one.

44:57 Yeah.

44:57 Yeah.

44:57 Yeah.

44:58 Yeah.

44:59 Yeah.

45:00 Yeah.

45:01 It's a big one.

45:02 Build in template tags and filters.

45:05 And yeah, there's a bunch of stuff there.

45:08 I think it's just good to, as you're writing a lot of templates, where you're going to reference the objects that you're passing from the views.

45:16 It's kind of handy to know how to do that.

45:19 So I will just go over a few ones we learned about and use.

45:24 So for example, you have a books, the list of book objects, and you look through them, but sometimes there are no books, right?

45:32 So instead of like doing if books, look over the books and else there are no books, you can actually do for empty.

45:40 And that's kind of a construct that Django has that if there are no objects, then it goes into the empty block.

45:48 It's just shorter, right?

45:49 Oh my God.

45:50 Is that proper use for like an else block on a for foreign loop?

45:54 Something like that.

45:55 It might be actually.

45:56 So you've got the syntax, you know, you've got the angle bracket percent for book in books, li curly curly book, right?

46:04 So you're that would be and then you would say and for and that would just print out a bunch of allies with the various books that were in the list.

46:10 Yeah.

46:11 Like you said, you're going to want to deal with it.

46:13 If it's not there.

46:14 So instead of doing a whole nother test and an else, like you can just say four books, li, then percent empty.

46:21 Sorry, there are no books and four.

46:23 And it's all like one continuous thing.

46:25 That's pretty cool.

46:26 I guess there's no shame in doing an if, if else performance wise, I guess, but it's just shorter code, right?

46:32 It's, it's more elegant.

46:33 And, again, it's, it all goes back to like knowing about those constructs.

46:37 Yeah. And it's more expressive. It's like, yeah, here's the thing to put when it's empty, not, oh, I see.

46:42 It means that if this thing is false, then we have nothing.

46:46 But if it's true, then we can go into its book list inside.

46:50 Like, it's just, it's more clear about like, here's the empty case.

46:53 So it's more explicit, right? Yeah.

46:55 Yeah. And also to get really nerdy, like in those templates, you get to a lot of indents already because HTML is like that.

47:02 If you properly format it, it's so bad.

47:04 So that extra if else you can save theirs, it's just nice, right?

47:08 Two to four spaces less. It's good.

47:10 Yeah.

47:11 Talk Python To Me is partially supported by our training courses.

47:16 How does your team keep their Python skills sharp?

47:19 How do you make sure new hires get started fast and learn the Pythonic way?

47:23 If the answer is a series of boring videos that don't inspire or a subscription service you pay way too much for and use way too little, listen up.

47:32 At Talk Python training, we have enterprise tiers for all of our courses.

47:36 Get just the one course you need for your team with full reporting and monitoring.

47:40 Or ditch that unused subscription for our course bundles, which include all the courses and you pay about the same price as a subscription once.

47:48 For details, visit training.talkpython.fm/business or just email sales@talkpython.fm.

47:54 Then what we use a lot is overriding blocks.

48:00 So in the base template, you can define a block, for example, title, title of the website.

48:06 And then it's again an example like Fast-Based Views of Inheritance.

48:10 Then if you go into the child template, you can just specify a block title and then give it some other content.

48:16 So in the base template, you kind of set the expectations.

48:20 There are going to be blocks of ABC.

48:22 And then in the child template, you can then actually populate those blocks.

48:26 And I think the advantage is that sometimes a title, for example, needs to go high up in the order of HTML.

48:34 So you have to kind of the order of the elements on the page, you have to kind of have it in your base template.

48:39 And so it's a nice way to set it there and then come back in the child and set it to something else.

48:44 That makes sense.

48:45 Yeah, I love these types of things that I have on all the sites that I run.

48:49 I have three or four that are always there.

48:51 Obviously, the main content, right?

48:53 Like whatever the page content is going to be, there's going to be one for that.

48:56 This title one is super important because you want to be able to set like literally the angle bracket title, but there's not really a good way to get into that space from a base template, a drive template, right?

49:06 It's got to like reach and poke a hole up there.

49:08 The two other that I always have are like extra CSS and extra JavaScript.

49:13 So like in this page, in this few pages, I need to have one more CSS file or I need to have like this one page is going to use Vue.js and be like a single page app.

49:22 But not the rest of the stuff.

49:23 So on this one, I'm going to like punch the Vue.js stuff at the end or something like that.

49:27 Oh, that's cool.

49:28 Yeah.

49:29 For example, maybe on your checkout page, you want to have to stripe JavaScript library, but not on all pages, right?

49:34 Yeah, not on all of them.

49:35 So you can do that extra JavaScript and then have that script import there, although it might not be nice to have that.

49:42 I mean, strict HTML, but you know.

49:45 No, no, I'm pretty sure like that's probably exactly what I have.

49:48 That's a perfect use case.

49:49 It's something like that only appears on a few pages.

49:51 You don't want to bundle the stripe.

49:53 I don't even know if it makes sense in terms of their API to like minify and bundle their JavaScript with yours.

49:59 It needs to come from their site live.

50:01 And so yeah, exactly.

50:02 But you want to stick it in the right place, not just randomly in the middle of the page.

50:06 So these little like block overrides are just perfect.

50:09 Yeah.

50:10 Cool, cool.

50:11 So never hard code URLs or static files.

50:14 You can set the static at the top of your template and then just reference static and then the relative URL or URL to link to another page on your web app.

50:26 You can just use the URL and then reference the name of the URL as you set that in your URLs.py.

50:33 So the example here, we have curly braces percentage and then URL keyword and then the string login and that then matches to the login path in the URLs.py because we named it that explicitly.

50:47 That's a good thing to know.

50:48 Don't go hard code URLs because you switch domain or whatever and that will break.

50:54 Right.

50:55 Yeah.

50:55 Yeah.

50:56 Super cool.

50:56 Other thing I like is filters.

50:58 So I have a tags.py file and we have a bunch of register.filter decorators.

51:06 So for example, here, we wrote one that is new with that receives a daytime and that looks if that daytime is less than seven days in the past.

51:18 So if something is a week old.

51:20 So and then we use it in the template.

51:22 For example, we have by dot edit and that's again that edit field that was with the outer now.

51:28 And then we can do pipe is new goes a bit into the weeds.

51:31 But basically applying that is new filter on a date in the template, you can kind of play with it.

51:39 And what we do, for example, is to give it a class and then I guess show a new image on it.

51:46 Put a little new badge or something on it.

51:47 Yeah.

51:48 Uh huh.

51:49 Yeah.

51:50 I guess if we post the snippet, it makes more sense.

51:52 But yeah, basically, it's a way to write a filter and you don't have to do a lot of if else and complex logic in the template.

52:00 But you have that actually in the back end and then you can just use it as on the object in the template.

52:07 I guess.

52:08 Yeah.

52:08 Let's show the code and it just.

52:10 Yeah.

52:11 But yeah, it's a cool way to basically put a little pipe filtering operator on there and it runs a little bit of custom code that you write.

52:18 Yeah.

52:19 You write it in Python.

52:20 It runs it in templates.

52:21 Yeah, exactly.

52:22 Yeah.

52:23 That's well stated.

52:24 I think.

52:25 Yeah.

52:26 Yeah.

52:27 So when I'm looking through this, you're talking about templates a lot.

52:30 A little while ago, we talked about how it was nice to start out with some code or when you're creating these pages, maybe the database is empty, but you want some stuff in the page because it's super hard to actually see what's going on and design the pages without some content.

52:47 So maybe some kind of lorem ipsum.

52:50 Yeah, that's one that comes out of the box, right?

52:53 I was so surprised that Django has lorem ipsum built into it.

52:58 Must be in high demand, you know?

53:00 Exactly.

53:01 Now, it would be better.

53:04 I mean, this is pretty good.

53:05 It would be better if it were hipster ipsum, which is like lorem ipsum, but speaks.

53:12 It speaks hipster speak, I guess you would call it.

53:17 Like Stumptown, XOXO, keytars, semiotics, PBR, and MP, street art, blue bottle before they sold out, pop up mixtape and so on.

53:27 Of course, Portland shows up in here.

53:29 That's awesome.

53:30 Yeah, yeah.

53:31 So anyway, if people are looking for like lorem ipsum filler, there's actually like a lorem ipsum operation like empty as well in Django.

53:39 Wow.

53:40 That's really cool.

53:41 Yeah, let me see.

53:42 Let me pull it really quick just to see the tag.

53:44 It is.

53:45 Yeah, you just do percent lorem count method randomness close percent close curly.

53:52 And boom, out it comes.

53:53 It's beautiful.

53:54 Yeah, like a lorem 2w random.

53:56 Well, I'll put two random Latin words.

53:58 How cool is that?

53:59 Yeah, so that's really cool actually that it's built right into the templates.

54:03 And what's nice is you don't have to have like paragraphs of junk that you're going to then delete.

54:07 Literally like just a little bit with like I want 20 words here.

54:10 Yeah.

54:11 Right.

54:12 So super cool.

54:13 Well, remember when you were maybe was recording one time like a pretty print of how much time a comment was posted like four days and six hours ago.

54:23 So I actually thought that was humanized and I guess humanized can do that.

54:27 But it's actually here on the text page time since.

54:31 Yeah.

54:32 Oh, really?

54:33 Like it takes a daytime and then it will like humanize it into half an hour ago, an hour ago, a week ago.

54:38 Yeah.

54:39 Oh, that is super cool.

54:40 Yeah.

54:40 Yeah, I love to see dates like that.

54:41 Yeah.

54:41 Yeah.

54:42 I use that on our on our messages and forums.

54:45 It's super nice.

54:46 This is really nice.

54:47 It's perfect nicer, you know, and don't have to write that logic, right?

54:50 You just comes in a template.

54:51 Perfect.

54:53 That's awesome.

54:54 We're spoiled batteries.

54:55 All right.

54:56 Are you ready for a bonus round?

54:57 Yeah.

54:57 Yeah.

54:58 Yeah.

54:59 Let's do a bonus round.

55:00 Let's do it.

55:01 All right.

55:02 So, yeah.

55:03 Bonus number one hit us.

55:04 Okay.

55:05 And I've not used this that much, but playing with it yesterday, it's actually pretty cool.

55:07 It's a Django extensions.

55:09 And I will just highlight a few things you can do.

55:12 You can do Python, manage the pie graph models, and then minus a, minus O, and then a PNG.

55:19 And it will make a PNG or ping, whatever you call it, of all your models in your database.

55:27 So it'll create like a UML diagram of your Django ORM models, basically.

55:32 Yeah.

55:33 Something like that and how they're linked together.

55:34 Yeah.

55:35 Mine is like super small.

55:36 I know if it's formally UML, but yeah, it's definitely more or less.

55:39 Yeah.

55:40 It's so small because there are so many models in our thing, but pretty cool.

55:44 Exactly.

55:45 Like how you can visualize that, I guess, if you would, you know, get more developers on

55:49 board.

55:50 Yeah.

55:51 And you talked about shell.

55:52 Yeah.

55:53 I mentioned shell before, and there's actually a shell underscore plus that comes with the Django

55:57 extensions.

55:58 And then you can also give it dash as IPython, and then you drop into an IPython shell.

56:03 And it also imports all your models.

56:06 So you don't have to import them manually, but it's pretty convenient.

56:09 And not part of the Django extension, but I did want to mention is a plugin called Django

56:16 dash tiny MCE, which gives you a rich text editing field.

56:21 And it's pretty sweet.

56:22 Yeah.

56:23 Yeah.

56:24 So tiny MCE is like a WYSIWYG, what you see what you get, HTML editor, right?

56:28 Yes.

56:29 Yeah.

56:30 So this like brings it in or something like makes it easy to use in Django.

56:32 Yeah.

56:33 Well, you would use the HTML field.

56:35 So from tiny MCE dot models import HTML field.

56:38 So it would of course cause another migration, but yeah, it's, it's pretty easy to use.

56:43 And, it's a huge improvement for the end user.

56:45 Right.

56:46 So, yeah, that's super cool.

56:47 You know, while you're on the topic of that, I'll throw one more out.

56:50 There is simple MDE.com.

56:52 So tiny MCE is cool, but I think the ultimate outcome is HTML and simple MDE is a beautiful

57:01 Markdown editor.

57:02 That's just pure JavaScript.

57:03 Yeah.

57:04 Pure client side JavaScript.

57:05 It has like hotkeys.

57:07 It has side by side secretized scrolling all like even the Markdown, like the text in Markdown,

57:13 even though it's plain text, it's like the headings are bigger than the code bits and whatnot.

57:19 It's, it's really easy to throw it on a text area.

57:21 Looks super nice.

57:22 Yeah.

57:23 Yeah.

57:24 Cool.

57:25 Yeah.

57:26 It's very nice.

57:27 Just turn off spell checker.

57:28 It'll tell you everything's misspelled.

57:29 All right.

57:30 Bonus number two.

57:31 Keep it flying.

57:32 salary.

57:33 Yeah.

57:34 Salary really want to mention that because, we're especially in the enterprise where I

57:38 see Django, I see salary.

57:39 Like it's just always tasks that are, that have to be offloaded and be done async wise.

57:45 It makes sense.

57:46 Right.

57:47 Right.

57:48 It's not meant to be done in the context of a direct request.

57:51 I learned this the hard way with my site.

57:53 So there's a backend that will let me email the people in a particular course.

57:59 Like, so I could go, if there's an update, like a problem with the course, I can go into

58:02 that course to email everybody who subscribed here.

58:05 And then eventually the first time I did, I didn't really think this all the way through.

58:08 I went in and I filled out an email, I hit go and it just sat there and it spun and it

58:13 spun and it spun.

58:14 And then eventually it timed out after like 20 seconds and crashed.

58:17 I'm like, Oh no.

58:18 So it emailed like thousands of people, but I don't know who actually got the email.

58:23 Cause I said everybody and it got partway through and then crashed.

58:26 And so then what I do, I email them again.

58:28 The first half will get the double.

58:29 The second half didn't get it at all.

58:31 Like I'm just like, what have I done?

58:33 What a fool.

58:34 Yeah.

58:34 So don't do that.

58:35 That's a bad experience.

58:36 But now your Sunday evening will be shorter, right?

58:38 Because you know that ORM and you can, but yeah, you would have to write some sort of

58:42 script to see who got emailed or not, you know?

58:45 Yeah, exactly.

58:46 Like scrape the logs or something, right?

58:48 Like I basically decided, well, I guess the other half of the class is just not getting that

58:52 message.

58:53 I'm sorry.

58:54 Yeah.

58:54 So now it's salary, right?

58:55 You would click send and you would be immediately back at the, from the request cycle.

59:00 So back at your interface.

59:02 Right.

59:03 Dump it in a queue, throw it at salary and say, start emailing these people.

59:06 It's on the queue and it's all happening in the background.

59:08 Yeah.

59:09 So that's, then, you know, for, for other things like heavy file processing, anything intensive,

59:16 send it to you, right?

59:18 Because the user shouldn't be waiting for that.

59:21 I think even for some of my stuff, I started doing logging on a queue.

59:24 I'm just like, I don't want this, whatever this is, I don't want it to like be part of

59:28 this request.

59:29 I'm just going to throw it over there and we'll get to it in a minute.

59:31 You know what I mean?

59:32 Yeah.

59:33 Yeah.

59:34 Yeah.

59:35 And a quick debugging tip.

59:36 If you want to debug salary, actually, sometimes it's convenient to not send it to a queue

59:40 and have it as part of your main process.

59:42 And you can set salary_always_eager equals true in your settings.

59:48 And then all the, then it doesn't go to a task queue anymore, but it goes straight to your main console where you have run server running.

59:55 And it's just easier to debug.

59:57 So I just want to mention that tip that it's not very well documented, but it definitely helped me on a few occasions.

01:00:04 Yeah.

01:00:05 Very cool.

01:00:06 That's awesome.

01:00:07 I definitely need to make better use of salary and what I'm doing as well.

01:00:08 All right.

01:00:09 Last one.

01:00:10 This one I just threw on there.

01:00:11 So I'll tell you about it, Bob.

01:00:12 I told you that there's like all these headers you should be putting into your code and to your HTTP response.

01:00:19 And if you go look at the OWASP top 10 web vulnerabilities, the open web security group or whatever that OWASP stands for, they go through and they keep track of all the problems.

01:00:30 Right.

01:00:31 And some of those can be, you know, cross site scripting, embedding your stuff and something that looks like your site, like an embed something that basically is your site in an iframe, but then put a transparent overlay that captures keystrokes for like say username and password, all sorts of bad stuff like that.

01:00:46 So there's all these headers you should send back.

01:00:48 So if you use this thing called secure PI, which is a github.com type slash type error slash your dot PI.

01:00:54 And I don't know how they got this, but it's pip install secure, which is incredible that that's still available.

01:00:59 But the idea is it just on any of these frameworks, including Django, it will just as middleware, just drop in all the changes you need.

01:01:08 And it's like for Django, let's see, what does it take?

01:01:11 Oh, that's just the Django.

01:01:12 Just the Django.

01:01:13 There's a super simple integration on how to set it up.

01:01:16 I had to pull up the docs.

01:01:17 Anyway, it's really super easy.

01:01:19 And then you get things like strict transport security, cross site protection, X frame options, the same origin, like all those kinds of things that you would like to have that you might forget.

01:01:28 And because it's middleware, it's just like magic.

01:01:30 Wow, that's really cool.

01:01:31 Yeah.

01:01:31 And it works with like 15 different frameworks or 10 different frameworks.

01:01:35 It's really almost no matter what you're using, it works with it, you know?

01:01:38 Yeah.

01:01:39 Wow.

01:01:40 And you just added to middleware with app dot secure middleware dot set secure headers.

01:01:44 Yeah.

01:01:45 Really cool.

01:01:46 Yeah.

01:01:47 It's literally like one function, like a simple decorator that you write more or less simple, simple middleware.

01:01:53 Yeah.

01:01:54 I just added in there and off it goes.

01:01:55 So it integrates just like you'd expect.

01:01:57 Anyway, that's just, it's so easy.

01:01:59 It's so easy to add that security.

01:02:01 And who knows if something else comes out that you're supposed to start doing, you know, secure dot PI might get updated and your site will just magically be more secure.

01:02:08 And you don't have to think about that, which is always nice.

01:02:10 Yeah.

01:02:11 Theoretically.

01:02:12 Pretty cool.

01:02:13 Thanks.

01:02:14 Yeah.

01:02:15 Yeah.

01:02:16 Yeah.

01:02:17 You bet.

01:02:18 Of course.

01:02:19 Of course.

01:02:20 All right.

01:02:21 Well, we did it.

01:02:22 We didn't take four hours to cover that, even though it did take some, some time.

01:02:23 I mean, we, you guys who are listening, you can't see there's a ton of details.

01:02:26 We kind of skipped over.

01:02:27 We could have gone into it.

01:02:28 Right.

01:02:29 Like, like you said, with manage that PI is probably its whole episode.

01:02:32 For example, we've got Django test, but Django rest framework.

01:02:36 We got wagtail.

01:02:37 We got century.

01:02:38 Then go channels.

01:02:40 We haven't even touched on authentication.

01:02:42 There's all sorts of stuff.

01:02:43 Right.

01:02:43 That you didn't mention century.

01:02:45 You didn't mention.

01:02:46 I did.

01:02:46 But yeah, that was an official item.

01:02:48 But yeah.

01:02:49 Yeah.

01:02:50 Yeah.

01:02:51 Yeah.

01:02:52 Century is cool.

01:02:53 There were some 500 errors.

01:02:54 We would have never known about.

01:02:55 Well, not affiliated or anything.

01:02:56 Oh, it's real.

01:02:57 Yeah.

01:02:58 They're so rare.

01:02:59 They're so rare.

01:03:00 And they're under weird edge cases.

01:03:01 And if you run a website, you should definitely install something like century or roll bar or something like that, where you get notifications anytime there's an error.

01:03:10 At first, it's annoying.

01:03:11 Like there's certain things that you can't control.

01:03:14 Well, somebody will try to, you know, post a binary thing to some part of your API where it expects like Latin characters.

01:03:22 You'll get like a non Latin parse area.

01:03:24 Like, okay, well, there's nothing I could do about this, but you could start muting those types of things.

01:03:28 And eventually you'll be like, oh, I got these errors.

01:03:31 And literally, I would say more, certainly more than one time, I have seen an error come in over one of those channels, figured out what was going on, fixed it and then sent a message to the user and said, I'm sorry, I saw you had this error.

01:03:44 Here's I fixed it.

01:03:45 And they're like, well, I was just about to write you.

01:03:47 This is crazy that you actually, I didn't even tell you there was an error and you fixed it and contacted me.

01:03:52 That's so cool.

01:03:53 Right?

01:03:53 Yeah.

01:03:54 Yeah.

01:03:55 Because often they 500 error, this sucks and they go away.

01:03:57 Right.

01:03:58 Exactly.

01:03:59 Exactly.

01:04:00 And now you're proactive about it.

01:04:01 So that, that, that really makes a difference.

01:04:03 Yeah.

01:04:03 And even if they're mad and they go away and they've lost faith, whatever, like, you know, there's nothing you can do to change the past, but it's, your site is better every time you catch one of these errors that you wouldn't have caught.

01:04:14 So yeah.

01:04:15 But I agree that a lot of time it's like edge cases, but sometimes there's something that, that might indicate a bigger problem.

01:04:20 That's, that's good to know.

01:04:21 Right.

01:04:22 Or you deploy something that's bad and all of a sudden it's like gone crazy and your email and text messages are full.

01:04:27 You're like, whoa, whoa, whoa, I better go check this out.

01:04:29 It's not good.

01:04:29 Yeah.

01:04:30 It feels a bit safer, right?

01:04:31 To do a Git push to having that, it's a bit safer.

01:04:35 Yeah.

01:04:36 Yeah.

01:04:37 At least you'll see the smoke if it catches on fire.

01:04:39 Yeah.

01:04:40 Yeah.

01:04:41 All right.

01:04:42 Well, these are really fun things to cover, Bob.

01:04:44 And I think people really enjoy them.

01:04:46 We can put together some of them just, and we'll try to link to the others in the show notes.

01:04:50 Yeah.

01:04:51 Cool.

01:04:52 We'll do.

01:04:53 I'll try to answer the final two questions.

01:04:54 Oh, so if you're going to write some Django code, what editor to use?

01:04:58 Yes.

01:04:59 I still use him.

01:05:00 Yeah.

01:05:01 I don't go away.

01:05:02 Yeah.

01:05:04 It's, I always say it's a steep learning curve, right?

01:05:07 the beginning was very awkward using that editor, but it has this compound effect.

01:05:13 Once you get to a lot of shortcuts and you just become pretty fast in it and it's very

01:05:18 convenient, but I'm missing out on all the ID features, I guess.

01:05:21 Yeah.

01:05:22 Yeah.

01:05:23 That's cool.

01:05:24 You want to hear a little Vim joke?

01:05:25 Yeah.

01:05:26 Yeah.

01:05:27 Okay.

01:05:28 Yeah.

01:05:29 I might.

01:05:30 Yeah.

01:05:31 So how, how do you generate a random string?

01:05:32 Yeah.

01:05:33 I know.

01:05:34 Trying to exit it.

01:05:35 You put a first year computer science student in Vim and ask them to exit.

01:05:39 Yeah.

01:05:40 That's so good.

01:05:41 Yeah.

01:05:42 Yeah.

01:05:42 It's all good.

01:05:43 Right.

01:05:44 And then we covered so many packages here, but maybe just throw out a couple of packages

01:05:48 that you're like, man, people need to know about this thing.

01:05:50 Yeah.

01:05:51 I'm going to, kind of abuse the fact that we had to leave some out to pull them in here.

01:05:57 So we didn't talk about, the bonus round, the bonus bonus.

01:06:00 So Django all out is a great package or plugin to, yeah, to really make your authentication

01:06:07 in Django easy.

01:06:08 And, and Django rest framework.

01:06:09 And, and Django rest framework.

01:06:11 I still find it a great, great package offer to, to build APIs in Django.

01:06:15 But if it doesn't have to be Django, then let's throw in Python date util, which is really cool.

01:06:22 Yeah.

01:06:23 Yeah.

01:06:24 Python date util is awesome.

01:06:25 I love its ability to parse stuff without me.

01:06:27 I think too much about it.

01:06:28 The fuzzy, fuzzy parsing, right?

01:06:30 The, yes, exactly.

01:06:31 It almost always works.

01:06:33 It is so nice.

01:06:34 Because you don't have to remember SDRP time.

01:06:37 You just throw it into parse and it magically, converts it to a daytime object.

01:06:43 Right?

01:06:44 Yeah.

01:06:45 Yeah, absolutely.

01:06:46 There's some site and I can't remember what it is.

01:06:47 I know I've covered on Python bytes, but unfortunately I'm not tracking it down right now.

01:06:51 There's a site that will let you type in a human readable string and then tell you the format string that goes with it.

01:06:57 Oh, it's so nice.

01:06:58 Oh no.

01:06:59 That's cool.

01:07:00 Yeah.

01:07:00 So you type like Wednesday at two, 2 PM and it'll, it'll actually pull it up.

01:07:05 Nah, I can't find it.

01:07:07 Oh, well anyway.

01:07:08 Yeah.

01:07:09 That's cool.

01:07:10 Long story short.

01:07:11 This is a great package.

01:07:12 If you're parsing things, Python dash date util.

01:07:14 Yeah.

01:07:15 And now can I mention another one?

01:07:16 Go for it.

01:07:17 So if you're working a lot with databases, which obviously happens when you, when you do web development,

01:07:20 and Django for Postgres, PG CLI.

01:07:24 PG CLI is like a way nicer interface than PSQL.

01:07:28 Yeah.

01:07:29 So just pick install it and you will have a lot more fun working with Postgres databases from the command line.

01:07:35 Nice.

01:07:36 Do you know one?

01:07:37 I know one that you recommended was SQLite browser at sqlitebrowser.org, which is pretty cool, but that's just for SQLite.

01:07:43 Right.

01:07:44 Have you come across Beekeeper Studio at beekeeper studio.io?

01:07:48 No.

01:07:49 Oh, my goodness.

01:07:51 So this is a free open source database GUI for Postgres, SQLite, SQL Server, presumably also Oracle works on all the platforms.

01:08:03 And man, it is cool.

01:08:04 You can even have like saved queries.

01:08:06 You typically run.

01:08:07 It has auto complete all kinds of stuff.

01:08:09 Wow.

01:08:10 That looks very nice.

01:08:11 It looks good.

01:08:12 Right.

01:08:13 It's something you look like you'd want to use.

01:08:15 Yeah.

01:08:16 Like even just an auto complete query thing.

01:08:19 Composer is pretty cool.

01:08:21 So anyway, we need to link to that as well.

01:08:23 Yeah.

01:08:24 We're going to throw out some database management stuff.

01:08:26 That's a really good one.

01:08:27 I should definitely.

01:08:27 I'll put that in there as well.

01:08:28 Cool.

01:08:29 All right.

01:08:30 We covered a lot of stuff.

01:08:31 What a whirlwind.

01:08:32 But it was really fun.

01:08:33 So yeah, I guess final call to action.

01:08:36 People are all excited about what you've talked about.

01:08:38 Also, maybe they're interested to see how you've taken some of these ideas and put them into action over at code challenges.

01:08:44 You know, final call action and what what should people do?

01:08:46 Yeah.

01:08:47 So we don't have bite exercises on Django.

01:08:50 That was a bit hard, but regardless.

01:08:52 Yeah, it's hard to set up the whole server and database infrastructure in like a little tiny bit.

01:08:57 Right.

01:08:57 That might be possible.

01:08:58 I think we have to have a flask bite.

01:09:00 So I think it's possible because we can install any packages on our AWS land us.

01:09:05 Right.

01:09:05 Right.

01:09:06 But regardless, go to code challenge.es and go write some code.

01:09:09 That's the best thing you can do if you want to learn Python.

01:09:12 And you can also reach out to me by our slack.

01:09:16 So if you go to by by the yes, they're on the resources.

01:09:19 There's a community link.

01:09:20 Anybody join your slack there?

01:09:21 Yeah, that's totally for the community.

01:09:23 You've got a really, really vibrant and self sustaining slack channel for Python developers like more than most other places I've seen, which is really cool.

01:09:31 I don't know.

01:09:32 You've got thousands of people in there, right?

01:09:33 Yeah, I think we crossed the 2000 mark.

01:09:36 And yeah, it's awesome how people are interacting, responding, helping each other.

01:09:42 It's it's it's such a nice place.

01:09:44 Yeah.

01:09:44 So they got a code challenge dot es slash community.

01:09:47 Is that it?

01:09:48 No, there is a slack but a logo on the platform.

01:09:52 But if you just want the link, then you can also just go to by by the yes.

01:09:57 And under the resources drop down, there's a community link.

01:10:00 And right, right, right.

01:10:01 Because slack sometimes the invite link.

01:10:03 Yeah.

01:10:04 Don't get me started.

01:10:05 Expires or whatever.

01:10:06 Don't get me started on getting joined into slack, man.

01:10:09 Yeah.

01:10:09 So we just have a dedicated page.

01:10:11 And if you go to by by by by the yes, then that always works.

01:10:15 Awesome.

01:10:16 All right.

01:10:17 Well, thank you for taking the time to come on here and talk all about this.

01:10:20 And congratulations on going full time on your project.

01:10:23 Awesome.

01:10:24 Good work.

01:10:24 Thank you.

01:10:25 Thanks for having me.

01:10:26 It was great fun.

01:10:27 Yeah, you bet.

01:10:28 As always.

01:10:29 See you.

01:10:30 Bye.

01:10:31 This has been another episode of talk Python to me.

01:10:33 Our guest on this episode was Bob builder boss, and it's been brought to you by Linode

01:10:37 and us over at Talk Python Training.

01:10:39 Start your next Python project on Linode's state of the art cloud service.

01:10:44 Just visit talkpython.fm/linode, L I N O D E.

01:10:49 You'll automatically get a $20 credit when you create a new account.

01:10:52 Want to level up your Python?

01:10:54 If you're just getting started, try my Python jumpstart by building 10 apps course.

01:10:59 Or if you're looking for something more advanced, check out our new async course that digs into all the different types of async programming you can do in Python.

01:11:07 And of course, if you're interested in more than one of these, be sure to check out our everything bundle.

01:11:12 It's like a subscription that never expires.

01:11:14 Be sure to subscribe to the show.

01:11:16 Open your favorite podcatcher and search for Python.

01:11:18 We should be right at the top.

01:11:20 You can also find the iTunes feed at /itunes, the Google play feed at /play, and the direct RSS feed at /rss on talkpython.fm.

01:11:28 This is your host, Michael Kennedy.

01:11:31 Thanks so much for listening.

01:11:32 I really appreciate it.

01:11:33 Now get out there and write some Python code.

01:11:35 Thank you.

Talk Python's Mastodon Michael Kennedy's Mastodon