← Back to Show Notes

Transcript: URLs: PKs, Slugs, & UUIDs

Will Vincent

Hello and welcome to another episode of Django Chat.

This week we're going to talk about URLs, IDs, slugs, UUIDs, and explain how it all works.

I'm Will Vincent and I'm joined as always by Carlton Gibson.

Hi, Carlton.

Carlton Gibson

Hello, Will.

Will Vincent

All right, so let's get into URLs.

So I'm asked a lot of questions around slugs in particular, which is sort of a Django-specific newspaper term, but we'll get into why URLs are confusing.

So basically, when you're building a web application, and especially if you're new to Django, you will have two major types of views.

You're gonna have a list view.

So let's take a blog application.

A list view will just show all the blog posts or some set subset of the blog posts.

And then you will have, if you use a generic class-based view in Django, a detail view.

So that's an individual page for just that post.

And this is where the question comes up of what do you put in that URL?

I would say the first place to start, you're making a No, no, no.

Carlton Gibson

I was just thinking about um I was just thinking through, I was just listening to you and thinking it through and thinking, yeah, I mean, for like the URL is like the interface, right?

So it's you've got it in your address browser, and sometimes they browsers now they don't show the URL fully.

I I'm I'm old school, I like to see, I like to see it.

But a a nicely designed URL scheme, it means that you've got say posts and then a forward slash and then an ID of some kind.

So like, you know, GitHub has GitHub, the the the the organization or the user, the the repo, the issue, and then issue number, and you can just adjust, you can get to a different issue number just by changing one little bit of the URL.

And it's like each of these path components is meaningful.

And you can kind of drive

Will Vincent

Yeah, and you can share it, and it helps with SEO versus a a fully, you know, uh full JavaScript setup, which is what you were describing before, which it which Google's starting to be able to figure out how to search, but it's

Carlton Gibson

but and you know, for uh it's part of one of the it's URLs are like the one of the major components of what the web is and uh what the internet is, I guess, but what what the web is.

And it's they're glorious.

I love them.

I'm just geeking out momentarily.

Will Vincent

Yeah, well I think and I think that's a I think until for me anyways, until I started building building websites, I never thought about URLs.

Uh I think your average you know consumer doesn't think twice about it, um, right?

I mean now they don't even have to remember them, they just type the name of the site into whatever search engine.

Um but they do matter.

They matter for developers who know what they're doing, who want to switch through the this the architecture, and it matters for SEO, and it just matters as a cleanliness structural thing.

And and one of these huge questions is so how do you, especially when you have a a detail page?

Um so an example would be so GitHub example.

So you'd have you know GitHub slash, for me it's WS Vincent, that's a list of all my repos.

And then what is it, what does it look like for that individual repo for Django X, which is my starter project.

You know, you could do that, you could have it just be Django X.

So that's um what GitHub does, that's basically like a slug.

Um so it's just putting the title into the URL, and you would often, if it's multiple names, you would have a uh a dash, not an underscore.

That's a point of confusion for you.

Well, underscore will isn't a dash.

I you can do both, but I I think it's I usually see dashes as the preferred method.

Do you have a opinion?

Carlton Gibson

No, really.

I just yeah, I mean dash is probably a bit more readable or something.

Will Vincent

There's something in the Django docs about, and I know this is like an ongoing debate among core people around like when to use underscores, when to use dashes, and some examples use one and not the other.

I don't know if that's fully been resolved yet in the documentation.

Carlton Gibson

For a Python identifier, a dash isn't a valid um it has to be an underscore because otherwise it wouldn't so if it's like a variable name or a module name or package names in Python code, it has to use an underscore.

That's why Python um

Will Vincent

But a URL, I would I I think that the default is a dash, not a URL.

So the name, so if you're naming if you're naming the URL, you would use an underscore.

If you're crafting the URL, maybe you'd have a dash in there normally for slugs.

Carlton Gibson

Yeah, if you've got a slug.

So what is a slug?

So um if you create a blog a blog post um model in Django and you add a slug field, and you can link it to the title, say, um, and with the um in the admin, for instance, it'll auto-p automatically populate the slug field if you tell it which field to base it on.

So you create um a blog post with a title, my great post, and then it will automatically put my dash great post, dash post in the slug field.

And that that slug field is is what will appear in the URL, is what you later then use in the URL.

Will Vincent

Right.

Well, and there's a there's a specific um slug field in the uh Django models, and there's also a there's a slugify function that will that will do that for the thing.

Carlton Gibson

Yeah, it takes a text string and um it returns a slug like string and you know the yeah.

Will Vincent

Yeah, so that's so you and uh you know I think with slugs too, there's so there's two things about it.

One is that you can do it in the admin.

So again, if you take you know Django is built for a newspaper, if someone's going in and making it, it will automatically um do it for them, or you can have them let them manually do it.

Actually, if you're doing it automatically, then you need to get into the the save method, right?

To override that to automatically do a slug, I believe.

Carlton Gibson

Yeah, I guess I mean if the slug is required, then yeah, it needs to be set.

Will Vincent

If it's required, I mean so if it's so if it's um if you're gonna have the creator do it in the admin, you can just have the slug field and they'll just type it.

But you can also, to your point, what's more common is based on the title or or whatever, you can auto-populate it and you do that by overriding the uh save method, which is a little black magic-y, but you get used to to that.

Um that's a common thing you'll you'll do in, I don't know, real-world Django applications, override the save method.

But it's can it's confusing and it's confusing in any case.

And then when you get to your URL, so now you're in your URLs.py file, you now after 2.0 you can use path instead of using uh regular expressions in there.

And you either will use the uh primary key or ID.

So so the Django uh models will uh Django databases will the Django ORM will automatically add an ID, an auto increment for you under the hood.

So your first blog post has a one, the second has a two, so on and so forth.

You can use that uh in the as the ID for a detail for a generic class-based view detail view.

But confusingly, you can also call it a uh primary key, um PK.

And I've actually I had a nice description of this, but I forgot off the top of my head.

The difference between ID and a PK.

I guess the difference between ID and a PK is you should probably use a PK, a primary key, because a primary key can refer to something other than an ID, like a UUID, which we'll get to.

Whereas an ID is very specific.

Also, ID has a specific meaning a lot in programming languages.

So when you are used referencing the ID, you should probably call it a primary key.

Carlton Gibson

Yeah, in Django, the PK shortcut is it it will it will pick out whatever field on the model happens to be the primary key.

Now, if you don't specify a primary key, it will be an auto-increment integer field, right?

Um Right.

Will Vincent

We'll get into changing that uh primary key.

Carlton Gibson

But if you've you might have used a text field, for instance, if you're just writing a small um contacts database just for your friends and family where you know you you're not gonna have conflicts, then there's no harm with just using the text field for name, the car field, the child field for name as the primary key because it's unique.

It's just Will and Carlton and you know Jessica and as long as conflict.

Will Vincent

That's the challenge with right, with your slugs, like you know, hello worlds, you know, you write two hello world posts, or you know, the reason to get back to GitHub, the reason GitHub partially solves this by it goes github.com slash user your username slash your repo name.

And so even if I have a repo called hello world and you have one called repo uh hello world, they're at different URLs because our usernames are different and you can't have two repos with the same uh name.

So that's a way that you can.

So Jet, it's very rare to have a slug that is just, you know, example.com slash slug.

Usually you want something prepended to I don't know, not make it unique, but make it distinguishable so you don't have these conflicts.

And if you do have these conflicts, you can do things like you can automatically add integers or strings onto the end, but that gets really messy.

Carlton Gibson

I mean, so you want either unique for user or unique for date or unique for some other field, right?

And you you can add validators on forms or models to to help you um enforce these.

So you you know um Django ships with unique for date and unique together and you know other other constraints on the model field.

Will Vincent

Yeah, and it used to be, you know, I don't know the and the SEO answer on this.

It used to be common that you would have the date in your, for example, your blog, but um that's not great for SEO in some ways.

And if you update your blog post, then it gets out of date.

So for

Carlton Gibson

me, like I think if you're using, if you're creating an actual web blog, an actual blog, uh like a historical record of what you've been doing that you put on the internet, then great, have a the date in the URL, because on you know, this date, I I wrote this post.

But people um came to realize that they wanted evergreen content, you know, particularly for marketing sites where they put out two or three really high-quality posts, which are evergreen content, which they're gonna hope to drive search traffic to and all these things.

You don't want the date in those posts because the date is irrelevant, they're evergreen.

Will Vincent

Right.

Or if you write Django tutorials like on my personal site and you update them to the latest version of Django, you don't want you know Django 2.2 on a post from 2016.

Carlton Gibson

It depends.

If is it a web blog or is it evergreen content, which the date isn't relevant for?

Will Vincent

Yeah.

So that brings us.

Will Vincent

So we talked about so the default is probably to use a primary key.

I think when I teach uh detailed views to people, I usually start with a uh primary key and then I'll discuss slugs.

Slugs are a little bit more complicated, you have to do with the admin or override the save method.

But there is another option that is probably a better choice, which is a universally unique identifier, a UUID, which Django added some really nice features around recently.

So do you want to explain what a UUID is, probably?

Carlton Gibson

So UUID is what this long string, um, you think of it as a long string that's is universally unique, right?

So it part of the there's we use UUID for is a particular algorithm for constructing you these unique identifiers.

And part of it is like um based on the MAC address of the machine that it was generated on.

Part of it is based on you know the time, you know, and so the chances of a conflict between these are microscopically slim.

And so, in a way that what ID one or ID two might not be unique, you the UUID will be unique.

And this is super good for if you're creating model instances in multiple places.

So let's say you've got a Django app with a server and everybody's creating, they're using traditional web requests and they're creating the instances on the server.

Well, primary key, integer primary keys are no problem because the database will ensure that the next one created will get the next primary key and you don't have to worry about it.

But then let's say you add a mobile client and that has offline capabilities where people are able to create instances on the mobile client and then sync them to the server later on.

All of a sudden, all of a sudden, you've got the danger of a of the same pri UUI or the same ID being created both on the mobile client and on the server at the same time by different requests.

And so the way you get around that is to use UUIDs because you know no matter where it was created, there's not going to be a conflict.

So later on, when you go to upload the the instance that was created on the client in an offline context to the server, you know there won't be a conflict of the ID.

Will Vincent

Yeah, and this is a little a bit similar to the mobile example when we talked about our authentication podcast, why using tokens rather than session IDs, again, this the synchrono this uh syncing issue crops up.

Uh another issue uh why a UUID is a good idea is if you have your ID hard-coded in the URL, like let's say, for example, I've got a list of you know clients and each one is an ID, and someone you know creates a new account and sees, oh, I'm client number 500.

Now they know exactly how many clients you have.

Um if you're at a banking site, all these issues, it's just too much information to display publicly.

It's it really is a security concern uh in most cases to just put the literal database ID in the URL.

I mean, for a blog, it doesn't matter, but uh if you're building a certainly anything enterprise or anything, anything charging money, uh you know, just for a security standpoint, a UUID is safer.

Carlton Gibson

Yeah, I mean you like and you don't want uh people to just be able to guess the URLs.

And UUIDs aren't really guessable either, right?

So you can't you couldn't

Will Vincent

Yeah, yeah.

Carlton Gibson

So if I give you a URL that's got ID 500 in it, and I was like, well, I wonder what ID 501 is.

Oh look, does that come up?

And whereas if if I gave you a UUID, you could type in any random string and it's not it's likely not to be an entry in the database.

Will Vincent

Yeah.

And then and then there's a further level of you actually share this with me, Carlton.

Um hash IDs.

So there's a hash IDs.org site and there's a Django hash ID field third-party package, but what is what is a hash ID?

Carlton Gibson

They um uh they enable you to have integer primary keys in the database, but then they create a nice short slug, which is you know half a dozen letters long, um, which isn't guessable.

It's not you can't go from one to two to three by just incrementing it because that the algorithm that generates the hash ID isn't guessable.

But that they're they're much shorter and nicer than um UUIDs.

UIDs are long and ugly and horrible, whereas six, seven characters, brilliant.

That's nice.

Um so hash IDs are lovely.

I I like them.

Carlton Gibson

They call they kind of solve the um they're exposable in a way that primary keys you might not worry about.

They're not um you use assault in them so that they're not predictable, but they're they still enable you to use integer primary keys under the hood.

Will Vincent

Yeah, they're nice.

And if you're um, you know, so how do I change, how do I go through step through this process?

This is actually uh a chapter in my book, Django for Professionals, which should be out now when this uh podcast is released.

Because it is, I think, you know, it really is tricky to go from ID to uh slug to UUID, let alone to hash ID.

And once you've done it all, you can sort of make these trade-offs and think about what to do.

But I think a takeaway is you can do these things.

If you are in doubt, use a UUID or a hash ID.

Carlton Gibson

Um, so

and it's not really that much more work if you do it for the switching over is a little bit of a pain.

Carlton Gibson

So if you've got a model which uses integer primary keys and you want to use a hash ID in and say an API, well, the the Django hash IDs package has a um REST framework serializer field which will um serialize the um the integer primary key to a hash ID and but vice versa.

So that will that will handle exposing in your API.

Um that's not that's that's great.

You can do the similar to put them into um template context if you need to do that.

If you need to migrate to a UUID, well, first thing, when you when you're coming up with your model, ask yourself this am I going to need to sync this?

If you are gonna need to sync it but from a mobile client, then use in the UUID to begin with.

Um if you're not going to, well, hey, just stick with an ID, it's easier, it's it's simpler.

Um if you do need to migrate, we'll probably add the feet add the UUID field, check everything's working when you've adjusted, and then switch over the primary key in the in the field definition and remove the um, which will create and then create a remigration which will remove the auto-created ID field for you.

So the Django migrations package will do that.

But do it slowly.

Add the UUID field first, adjust your API, make sure everything's working, and then switch over because

Will Vincent

Yeah, it's not a it's not a small undertaking.

Um, not to mention uh yeah, existing API endpoints or existing pages.

Carlton Gibson

Try and think about it in advance if you can.

Will Vincent

It's slightly more complex, but I would say when in doubt, just default to a UUID or a hash ID, and you'll future proof it.

Um, a little bit like using a custom user model for most people.

You could also do profiles.

Um you can change it later downstream, but it's cool.

Carlton Gibson

Yeah, and so I guess I think the general advice when you're designing your application, think about your URL structure.

Think what you want your URLs to look like, spend a bit of time because cool I cool cool URLs, they don't change.

They they they stay the same forever and they're they're reliable and they're addressable, and you can bookmark that and you can go back to it.

So think about your URL structure and try and design your application nicely around your URLs.

Will Vincent

Yeah, and I would I would say that with a bit of experience, you know, after the model, after the schema, the URLs is the second most important thing I think about in terms of architecting a project.

Um because yeah, the pages themselves that can change, but really it's you know that that yeah, hopefully that doesn't change as much as even the pay, you know, the views for what's displayed on the page itself, that's more likely to change than your underlying URL structure.

Carlton Gibson

Yeah, and I think of URLs as well as like a power tool for power users of your site.

Like so it's like the command line interface on a you know on your computer.

If you fire up the terminal and you can drive your computer from the command line, you can do things very quickly and very powerfully that you that might be more long-winded via the GUI.

Now the GUI is obviously easier, and that's great, and it's more accessible, and we love GUIs, but sometimes that power tool is exactly what you need.

And if you've got a really nice URL structure, it just enables people who are really into your site and into your application to use it more efficiently.

Will Vincent

Agreed.

So this wasn't the longest episode, but I think it covers an important point and something that trips people up.

And uh, you know, hash IDs in particular, if you're already familiar with UUIDs, hash IDs are really cool and worth looking at.

Carlton Gibson

It's a nice little library, it's a nice little tool if you you know it's this middle ground between the two.

Will Vincent

Yeah, exactly.

Um so as always, you can reach us at the DjangoChat.com website.

We're on Twitter at chatjango.

If you like this podcast, please also uh leave a review on whatever service you use.

We've received some really nice reviews, but reviews help people find our work and keep us motivated to keep doing these.

And that's it.

Anything else you want to add,

Carlton Gibson

um questions.

Well, you know, just send in some things.

We'll have a we should have an episode on user questions or read listener questions.

Will Vincent

Yes, actually, we should.

We we we've been getting a number and we've done a couple that are full length episodes, like the admin, because we've got to ask a couple questions on that.

But especially if there's it can be a small question, that would be fun to do a grab bag of um of user questions.

So send those in.

All right, we'll see everyone next time.

Thanks for listening.

Carlton Gibson

All right, take care, bye bye.

Bye.