Thisis the first full web application project for every software engineering trainee at Alx Africa. It is a hands-on practice showing the implementation of all the fundamental concepts covered in the Alx high-level programming track.
To my successors in Alx who are doing or are yet to do this project, the purpose of this article is to provide guidance and insight on how to approach this task. Please note that the solutions provided are not intended to be copied and pasted. You are tasked with coming up with solutions by yourself to meet the learning objectives. You are aware that plagiarism is strictly forbidden, and any form of cheating or academic dishonesty is unacceptable. Cheating in learning is simply self-deception, and that's downright dangerous, honestly!
While I will do my best to guide you through the task, I will not provide a complete solution. Instead, I will be available to tutor and have a totally "free" learning session with anyone who wishes to learn. You can reach out to me via Twitter or LinkedIn.
By continuing to read this article, you agree to adhere to the above guidelines and understand that any violation will result in your removal from the program. So without further ado, let's roll up our sleeves as usual and dive in.
The console is a command interpreter similar to a Python standard, interactive shell, or REPL. It allows us to run commands and interact with the web application through the command-line interface (terminal). Through this command interpreter, we aim to have a way to handle the objects in our project. This includes creating new objects, such as a new user or a new place, retrieving objects from a file or database, performing operations on objects such as counting and computing statistics, updating object attributes, and finally destroying objects when necessary. It's also very useful to track down bugs or logic errors because we can run and experiment with our web application on the console with different inputs.
We are building a web application where people can book a temporary apartment. Everything in Python is an object, which implies that they are an instance of a certain class. So, every entity in our Airbnb web application, including a person or people who want to book an apartment using this web application we are building in Python will be represented as an instance of a certain class in the database. An instance (object) of the User class in our database represents an individual who has created an account on this web app and contains relevant information such as name, email, and password. then an instance (object) of the class State represents a state or province, while an instance of the class City represents a city within that state, there will also have an instance of the class Place that represents a temporary room or suite available for booking on the web app and contains relevant information such as its name, description, location, price, and availability dates.
When a user wants to book an apartment, he or she will first create an account on our Airbnb web application, and an instance of the User class will be created and stored in the database, containing information such as the user's name, email, and password which are used to authenticate the user, retrieve their personal information, and process their bookings.
So now, to test the flow of this implementation, we are building a console that will allow us to interact with the User object from our end as developers through a command interpreter. The console will provide a command-line interface where we, the developers, can create new User objects by inputting the required attribute values (such as name, email, and password), view the output to confirm that the object was properly instantiated, and modify the attributes to test the update functionality.
This way, we can ensure that the flow and implementation of these objects are working correctly and according to our expectations. As a user on the web application tries to modify their details, such as their password, we can see if the updates are being properly reflected in the User object.
-> Interactive prompt handling: readline allows the shell to display a prompt to the user, indicating that the shell is ready to accept input. The user can then type a command or other input, which the shell will interpret and execute.
-> Command line editing: readline provides functionality for editing the current command line (i.e., the line of text currently being entered by the user), including features such as moving the cursor, deleting characters, and inserting text.
-> Command completion: readline can also provide tab-completion functionality, which suggests possible completions for the current command or argument based on previously entered commands or other contextual information.
I just defined a class for the console, and I set the prompt to (Airbnb) which means that when I run this file on my terminal, I will get a command prompt (Airbnb) just like a Python interactive shell prompt >> ready to take in commands and give outputs. Also, notice the whitespace before the last double quote; at least there should be a space after the prompt to separate your command interpreter prompt from the commands you will input.
You can also see that from this definition, class Myconsole(cmd.Cmd):, our console inherits from a base class, cmd.Cmd. This means that we now have access to the wide range of features and capabilities in the cmd module that can be customized and extended as needed, and one of them is the use of the readline library by the cmd module to provide functionality for handling user input from the console. This now makes it possible for us to implement or replicate these functionalities by defining some methods (methods in Python are somewhat similar to functions, except they are associated with objects and classes) in/under this class to implement our own logic according to the project requirements.
uuid module: Uuid stands for "Universally Unique Identifier." It's a 128-bit identifier that is unique across both space and time. What this entails is that, when we implement it, we will be able to generate hexadecimal digits (959902c6-6bc1-46c9-8b78-e93d6d67aa8c) that are unique across space and time, and the probability of two UUIDs being the same is so low as being impossible. These digits are the ID we will use to identify our objects, and at some point in the project we will have a database, so we need something to uniquely identify the data in each row in our database.
You can see that the output gave us some hexadecimal digits, and this will be unique among the IDs of any instance of that class. You might also notice that I converted the UUID to a string; this is strictly recommended to ensure that it can be easily stored and retrieved as needed.
Before now, I talked about the reason or need why we are building a console, where I said that "we aim to have a way to handle the objects in our project; this includes creating new objects, such as a new user or a new place, retrieving objects from a file or database, performing operations on objects such as counting and computing statistics, updating object attributes, and finally destroying objects when necessary." So, this module provides us with a convenient way to work with date and time values and allows us to accurately track the creation and modification times of each instance or object of our class. It provides a timestamp for each time we create a new instance or modify its attributes. I will explain extensively when we start writing the base model for this console, the logic involved in implementing this, and how we dynamically assign this module to every instance of our class so that it gives us the current timestamp when an instance (object) is created or modified. But here is a little snippet of it:
I created a dynamic attribute created_at, and assigned it a value at runtime in the constructor __init__() using the datetime.now() method, which gets the current date and time at the moment of instantiation. This means that the value of created_at will be different for each instance of BaseModel because it will be set to the time at which the instance was created. The datetime values you see in the output are year, month, day, hour, minute, seconds, and microseconds.
You can see from the output that it is now a str type object instead of datetime.datetime object type. We can also achieve the same result by using isoformat(). We simply call the isoformat() method on the instance of datetime.now()
*args and **kwags: seeing these for the first time in Python without knowing what they are was a little daunting because I thought they were pointers. Lol! But fear not! They are our friends. These are special parameters in Python, they are arbitrary arguments used when we are unsure of the number of arguments we will pass into our function. The names "args" and "kwargs" don't matter; they can be replaced with any name or valid identifier (variable) of your choice. What matters is the use of an asterisk (*) and double asterisks (**) to denote these special parameters.
We will not be using the (*args) parameter in this project, but for the sake of a complete concept explanation, let's see what *args are all about. *args is for multiple numbers of positional arguments that will be parsed in later, maybe during instantiation (creation of object) in classes, while **kwags is for multiple numbers of keyworded or key-value paired arguments that will be parsed later, just like *args maybe during instantiation (creating an object) in classes.
From the above code snippet, you can see that using the special parameter *args made it possible for me to parse in as many arguments as I wanted to the instance of my class. Likewise, with the keyworded special parameter **kwargs, I was able to parse in key-value paired arguments during the instantiation of my object.
3a8082e126