To learn more about the concepts covered in this lesson, check out:
Refactoring Your Code
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.
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.
It’s the perfect location to create the random string that you need for your
Instead of hard-coding the letters A to Z, you use the
string module and combine all ASCII uppercase characters and digits into
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.
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.
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.
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.
Go ahead and add
create_db_url(). First, the necessary imports from
sqlalchemy and other parts of the project are performed.
create_db_url() function is written, creating a
secret_key from the
create_random_key() function you just wrote.
db_url is created containing the needed information …
and the required database operations are performed. Finally, the function returns the
db_url object. Take note of one issue in this code implementation.
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.
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.
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.
Move back to
keygen.py and add
Session is imported from
sqlalchemy, and the
crud file you created earlier is also imported.
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.
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.
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.
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.
There are two advantages to creating
secret_key like this. Firstly, the
key prefix indicates which shortened URL
secret_key belongs to.
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
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.
You need to import
crud to use the functions you just created, so it’s added to the import statement
here you call
crud.create_db_url(). You get the
db_url() database object back, and you can use its fields
db_url.secret_key in the following lines.
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
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.
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.
Go to the
/docs URL and try out your API endpoints in the browser.
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
.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.
@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?