Locked learning resources

Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

Locked learning resources

This lesson is for members only. Join us and get access to thousands of tutorials and a community of expert Pythonistas.

Unlock This Lesson

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.

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.

Avatar image for Alexander Wendland

Alexander Wendland on March 5, 2023

Does this not implement circular logic in the code? crud imports from keygen and keygen from crud?

Avatar image for Darren Jones

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.