Refactoring Your Code
To learn more about the concepts covered in this lesson, check out:
00:00 Refactor Your Code. In this section, you’ll create two new files to separate the concerns of your app. Along the way, you’ll define functions to single out responsibilities.
00:13
You’ll end up with a cleaner create_url()
that’s much more readable. Start by creating a new file named keygen.py
. This file will contain all helper functions to generate keys for your project.
00:28
It’s the perfect location to create the random string that you need for your .url
and .admin_url
attributes.
00:48
Instead of hard-coding the letters A to Z, you use the string
module and combine all ASCII uppercase characters and digits into chars
.
00:57
Then you use the secrets
module to randomly choose five characters from chars
and return the selection. You could accomplish a similar result by using the random
module, as before. However, the secrets
module is recommended when creating random strings that you use as secret keys.
01:17
PEP 506 introduced secrets
as the standard Python module for generating cryptographically secure random bytes and strings. Check out the Guide to Generating Random Data in Python to learn more about generating random data.
01:35 By modularizing the random string creation to its own function, you can test it conveniently in the Python REPL.
01:48
When you call create_random_key()
without any arguments, you receive a string with five characters. In your case, the string is probably different from the string seen on-screen, but it should contain uppercase letters, numbers, or both.
02:06
Next, create a crud.py
file. Your crud.py
file will contain functions that perform actions to Create, Read, Update, and Delete items in your database.
02:20
Go ahead and add create_db_url()
. First, the necessary imports from sqlalchemy
and other parts of the project are performed.
02:37
Then the create_db_url()
function is written, creating a key
and secret_key
from the create_random_key()
function you just wrote.
02:53
A db_url
is created containing the needed information …
03:04
and the required database operations are performed. Finally, the function returns the db_url
object. Take note of one issue in this code implementation.
03:19
Remember that your database entry’s key value must be unique. Although the chances are minor, it’s possible for keygen.create_random_key()
to return a key that already exists.
03:30
So you need to make sure that there’s no entry with the same key
. First, define a function that tells you if a key already exists in the database.
03:57
This function returns either None
or a database entry with a provided key. Now you can create a function that makes sure you generate a unique key.
04:11
Move back to keygen.py
and add create_unique_random_key()
. First, Session
is imported from sqlalchemy
, and the crud
file you created earlier is also imported.
04:38
The while
loop is the most crucial part of this function. You’re calling create_random_key()
again if key
already exists in the database. Using this logic makes sure that every shortened URL exists only once.
04:55
With this function in place, update your create_db_url()
function in crud.py
. First you call keygen.create_unique_random_key()
to get a unique string for your shortened URL’s key. By calling this function, you ensure that there are no duplicate keys in the database.
05:22
Note that you’re calling keygen.create_random_key()
to construct a secret_key
string. As you saw before, keygen.create_random_key()
only creates a random string, but you don’t check if it exists in the database.
05:36
But you can be sure that secret_key
is unique because you are prefixing the string with the value of key
. So even if keygen.create_random_key()
returns an already-created string, then putting the unique key up front makes the entire string unique.
05:53
There are two advantages to creating secret_key
like this. Firstly, the key
prefix indicates which shortened URL secret_key
belongs to.
06:03
And secondly, you’re not hitting the database again when creating another random string. Head back to main.py
and update create_url
to use crud.create_db_url()
.
06:19
First, remove the import of the secrets
module. Because you don’t use secrets
directly in main.py
, you no longer need to import it here.
06:28
You need to import crud
to use the functions you just created, so it’s added to the import statement
06:48
here you call crud.create_db_url()
. You get the db_url()
database object back, and you can use its fields db_url.key
and db_url.secret_key
in the following lines.
07:09
Next, leverage the creation of get_db_url_by_key()
and update forward_to_target_url()
. Here you are updating forward_to_target_url()
to use crud.get_db_url_by_key()
.
07:26
This is a great chance to use an assignment expression (:=
), otherwise known as the walrus, and streamline the if
statement. The walrus operator gives you a new syntax for assigning variables in the middle of expressions. If you’d like to know more about the walrus operator, check out this Real Python video course.
07:49
If db_url
is a database entry, then you return your RedirectResponse
to the target_url
. Otherwise, you call raise_not_found()
. With all these updates in place, it’s time to check if your Python URL shortener still works as expected.
08:09
Go to the /docs
URL and try out your API endpoints in the browser.
08:18
Your API functions in the same way as at the end of the last step. But your code is much cleaner now. Still, you are not returning URLs, as the attributes .url
and .admin_url
would suggest. Instead, you’re only returning the keys.
08:37 You’ll create proper URLs in the next section of the course, where you’ll also add some functionality to let your users manage their shortened URLs.
Darren Jones RP Team on March 7, 2023
@Alexander Wendland - no, because the functions that are being imported don’t have circular references - the function in keygen that’s used in crud doesn’t reference any code from crud and vice versa.
Become a Member to join the conversation.
Alexander Wendland on March 5, 2023
Does this not implement circular logic in the code? crud imports from keygen and keygen from crud?