├── .gitignore ├── Flask-JWT Configuration Tutorial.md ├── README.md ├── section2 ├── 10_args_and_kwargs.py ├── 11_passing_functions.py ├── 12_decorators.py ├── 1_variables_methods.py ├── 2_lists_tuples_sets.py ├── 3_loops.py ├── 4_if_statements.py ├── 5_list_comprehension.py ├── 6_dictionaries.py ├── 7_classes_objects.py ├── 8_static_class_methods.py └── 9_inheritance.py ├── section3 ├── app.py └── templates │ └── index.html ├── section4 ├── app.py ├── security.py └── user.py ├── section5 ├── app.py ├── create_table.py ├── item.py ├── security.py └── user.py └── section6 ├── app.py ├── db.py ├── models ├── __init__.py ├── item.py ├── store.py └── user.py ├── resources ├── __init__.py ├── item.py ├── store.py └── user.py └── security.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.pyc 2 | .idea/ 3 | __pycache__/ 4 | *.db 5 | .DS_Store 6 | -------------------------------------------------------------------------------- /Flask-JWT Configuration Tutorial.md: -------------------------------------------------------------------------------- 1 | ## Overview 2 | This tutorial will cover some basic Flask-JWT configurations and help you customize it within your project. However, this tutorial will assume that you’ve followed the lectures and has set up Flask-JWT already. 3 | 4 | ## Before We Start 5 | First, let’s take a look at what we already have here. 6 | In our app.py file, we should already set up the JWT using the below code: 7 | ``` 8 | from flask_jwt import JWT 9 | from security import authenticate, identity 10 | jwt = JWT(app, authenticate, identity) # /auth 11 | ``` 12 | And in our security.py file, we should have something like this: 13 | 14 | ``` 15 | from werkzeug.security import safe_str_cmp 16 | from models.user import UserModel 17 | 18 | def authenticate(username, password): 19 | user = UserModel.find_by_username(username) 20 | if user and safe_str_cmp(user.password, password): 21 | return user 22 | 23 | def identity(payload): 24 | user_id = payload['identity'] 25 | return UserModel.find_by_id(user_id) 26 | ``` 27 | ## Configuration 28 | ### Authentication URL 29 | If we want to change the url to the authentication endpoint, for instance, we want to use ```/login``` instead of ```/auth```, we can do something like this: 30 | ``` 31 | app.config['JWT_AUTH_URL_RULE'] = '/login' 32 | jwt = JWT(app, authenticate, identity) 33 | ``` 34 | We added the second line of code to emphasize that we must config JWT first 35 | before requesting a JWT instance, otherwise our confuguration won't take effect. The following configurations follow the same principle but we will omit the second line. 36 | ### Token Expiration Time 37 | ``` 38 | # config JWT to expire within half an hour 39 | app.config['JWT_EXPIRATION_DELTA'] = timedelta(seconds=1800) 40 | ``` 41 | ### Authentication Key Name 42 | ``` 43 | # config JWT auth key name to be 'email' instead of default 'username' 44 | app.config['JWT_AUTH_USERNAME_KEY'] = 'email' 45 | ``` 46 | ### Other Configurations 47 | You may find out more configuration options here: https://pythonhosted.org/Flask-JWT/ 48 | 49 | Please refer to the \ section. 50 | 51 | ## More 52 | ### Retrieving User From Token 53 | Another frequently asked question is that how can I get the user's identity from an access token (JWT)? Since in some cases, we not only want to guarantee that only our users can access this endpoint, we want to make sure who he is as well. For example, you want to restrict the access to a certain user group, not for every user. In this case, you can do something like this: 54 | ``` 55 | from flask_jwt import jwt_required,current_identity 56 | 57 | 58 | class User(Resource): 59 | 60 | @jwt_required() 61 | def get(self): # view all users 62 | user = current_identity 63 | # then implement admin auth method 64 | ... 65 | ``` 66 | Now this endpoint is protected by JWT. And you have access to the identity of whom is interacting with this endpoint using ```current_identity``` from JWT. 67 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # REST APIs with Flask and Python 2 | 3 | This repository contains code that is created in my course, REST APIs with Flask and Python. 4 | 5 | The code is divided in folders, one for each section of the course which contains code. 6 | 7 | ## Section 2 8 | 9 | The code is in files numbered between 1 and 11, covering concepts ranging from beginner to advanced. 10 | 11 | 1. Variables 12 | 2. Methods 13 | 3. Lists, tuples, and sets 14 | 4. If statements 15 | 5. List comprehension 16 | 6. Dictionaries 17 | 7. Classes and objects 18 | 8. Static and class methods 19 | 9. Args and Kwargs 20 | 10. Passing functions as arguments 21 | 11. Decorators 22 | 23 | ## Section 3 24 | 25 | The code in this section includes a simple Flask app and a HTML and JavaScript file which calls the Flask app endpoints. 26 | 27 | ## Section 4 28 | 29 | The code in this section includes a Flask app which is an API that represents items. It also includes user registration and authentication. 30 | 31 | ## Section 5 32 | 33 | The code in this section extends the last section by adding persistent storage of Items to a SQLite database. 34 | 35 | ## Section 6 36 | 37 | The code in this section extends the previous section by replacing the manual integration with SQLite, with SQLAlchemy—an ORM (Object-Relational Mapping)—which allows us to easily replace SQLite with something like PostgreSQL or MySQL. 38 | -------------------------------------------------------------------------------- /section2/10_args_and_kwargs.py: -------------------------------------------------------------------------------- 1 | def my_method(arg1, arg2): 2 | return arg1 + arg2 3 | 4 | def my_really_long_addition(arg1, arg2, arg3, arg4, arg5): 5 | return arg1 + arg2 + arg3 + arg4 + arg5 6 | 7 | my_really_long_addition(13, 45, 66, 3, 4) 8 | 9 | def adding_simplified(arg_list): 10 | return sum(arg_list) 11 | 12 | adding_simplified([13, 45, 66, 3, 4]) 13 | 14 | # But you need a list :( 15 | 16 | def what_are_args(*args): 17 | print(args) 18 | 19 | what_are_args(12, 35, 64, 'hello') 20 | 21 | def adding_more_simplified(*args): 22 | return sum(args) # args is a tuple of arguments passed 23 | 24 | adding_more_simplified(13, 45, 66, 3, 4) 25 | 26 | ### 27 | 28 | # As well as a tuple of args, we can pass kwargs 29 | 30 | def what_are_kwargs(*args, **kwargs): 31 | print(args) 32 | print(kwargs) 33 | 34 | what_are_kwargs(name='Jose', location='UK') 35 | what_are_kwargs(12, 35, 66, name='Jose', location='UK') 36 | 37 | # args are a tuple 38 | # kwargs is a dictionary 39 | # This will come in handy! 40 | -------------------------------------------------------------------------------- /section2/11_passing_functions.py: -------------------------------------------------------------------------------- 1 | def methodception(another): 2 | return another() 3 | 4 | def add_two_numbers(): 5 | return 35 + 77 6 | 7 | methodception(add_two_numbers) 8 | 9 | ### 10 | 11 | methodception(lambda: 35 + 77) 12 | 13 | my_list = [13, 56, 77, 484] 14 | list(filter(lambda x: x != 13, my_list)) # A lambda function is just a short, one-line function that has no name. 15 | 16 | # We could also do 17 | 18 | def not_thirteen(x): 19 | return x != 13 20 | 21 | list(filter(not_thirteen, my_list)) 22 | 23 | # filter() passes each element of my_list as a parameter to the function. 24 | # Pretty neat, eh? 25 | -------------------------------------------------------------------------------- /section2/12_decorators.py: -------------------------------------------------------------------------------- 1 | # A decorator is just a function that gets called before another function 2 | 3 | import functools # function tools 4 | 5 | def my_decorator(f): 6 | @functools.wraps(f) 7 | def function_that_runs_f(): 8 | print("Hello!") 9 | f() 10 | print("After!") 11 | return function_that_runs_f 12 | 13 | @my_decorator 14 | def my_function(): 15 | print("I'm in the function.") 16 | 17 | my_function() 18 | 19 | 20 | ### 21 | 22 | def my_decorator(f): 23 | @functools.wraps(f) 24 | def function_that_runs_f(*args, **kwargs): 25 | print("Hello!") 26 | f(*args, **kwargs) 27 | print("After!") 28 | return function_that_runs_f 29 | 30 | @my_decorator 31 | def my_function(arg1, arg2): 32 | print(arg1 + arg2) 33 | 34 | my_function(56, 89) 35 | 36 | ### 37 | 38 | def decorator_arguments(number): 39 | def my_decorator(f): 40 | @functools.wraps(f) 41 | def function_that_runs_f(*args, **kwargs): 42 | print("Hello!") 43 | if number == 56: 44 | print("Not running!") 45 | else: 46 | f(*args, **kwargs) 47 | print("After") 48 | return function_that_runs_f 49 | return my_decorator 50 | 51 | @decorator_arguments(56) 52 | def my_function(): 53 | print("Hello!") 54 | 55 | my_function() 56 | -------------------------------------------------------------------------------- /section2/1_variables_methods.py: -------------------------------------------------------------------------------- 1 | a = 5 2 | b = 10 3 | my_variable = 56 4 | any_variable_name = 100 5 | 6 | string_variable = "hello" 7 | single_quotes = 'strings can have single quotes' 8 | 9 | print(string_variable) 10 | print(my_variable) 11 | 12 | # print is a method with one parameter—what we want to print 13 | 14 | def my_print_method(my_parameter): 15 | print(my_parameter) 16 | 17 | my_print_method(string_variable) 18 | 19 | def my_multiplication_method(number_one, number_two): 20 | return number_one * number_two 21 | 22 | result = my_multiplication_method(a, b) 23 | print(result) 24 | 25 | print(my_multiplication_method(56, 75)) 26 | 27 | my_print_method(my_multiplication_method('b', 5)) # What would this do? 28 | -------------------------------------------------------------------------------- /section2/2_lists_tuples_sets.py: -------------------------------------------------------------------------------- 1 | my_variable = 'hello' 2 | my_list_variable = ['hello', 'hi', 'nice to meet you'] 3 | my_tuple_variable = ('hello', 'hi', 'nice to meet you') 4 | my_set_variable = {'hello', 'hi', 'nice to meet you'} 5 | 6 | print(my_list_variable) 7 | print(my_tuple_variable) 8 | print(my_set_variable) 9 | 10 | my_short_tuple_variable = ("hello",) 11 | another_short_tuple_variable = "hello", 12 | 13 | print(my_list_variable[0]) 14 | print(my_tuple_variable[0]) 15 | print(my_set_variable[0]) # This won't work, because there is no order. Which one is element 0? 16 | 17 | my_list_variable.append('another string') 18 | print(my_list_variable) 19 | 20 | my_tuple_variable.append('a string') # This won't work, because a tuple is not a list. 21 | my_tuple_variable = my_tuple_variable + ("a string",) 22 | print(my_tuple_variable) 23 | my_tuple_variable[0] = 'can I change this?' # No, you can't 24 | 25 | my_set_variable.add('hello') 26 | print(my_set_variable) 27 | my_set_variable.add('hello') 28 | print(my_set_variable) 29 | 30 | 31 | ###### Set Operations 32 | 33 | set_one = {1, 2, 3, 4, 5} 34 | set_two = {1, 3, 5, 7, 9, 11} 35 | 36 | print(set_one.intersection(set_two)) # {1, 3, 5} 37 | 38 | print({1, 2}.union({2, 3})) # {1, 2, 3} 39 | 40 | print({1, 2, 3, 4}.difference({2, 4})) # {1, 3} 41 | -------------------------------------------------------------------------------- /section2/3_loops.py: -------------------------------------------------------------------------------- 1 | my_string = "hello" 2 | 3 | for character in my_string: 4 | print(character) 5 | 6 | for asdf in my_string: 7 | print(asdf) 8 | 9 | my_list = [1, 2, 5, 3, 67] 10 | 11 | for number in my_list: 12 | print(number) 13 | 14 | for number in my_list: 15 | print(number ** 2) 16 | 17 | should_continue = True 18 | while should_continue: 19 | print("I'm continuing!") 20 | 21 | user_input = input("Should we continue? (y/n)") 22 | if user_input == 'n': 23 | should_continue = False 24 | -------------------------------------------------------------------------------- /section2/4_if_statements.py: -------------------------------------------------------------------------------- 1 | my_known_people = ["John", "Rolf", "Anne"] 2 | user_name = input("Enter your name: ") 3 | if user_name in my_known_people: 4 | print("Hello, I know you!") 5 | 6 | 7 | if user_name in my_known_people: 8 | print("Hello {}, I know you!".format(user_name)) 9 | 10 | 11 | if user_name in my_known_people: 12 | print("Hello {name}, I know you!".format(name=user_name)) 13 | 14 | "Hello {name}, I know you {}!".format("well", name=user_name) 15 | "Hello {}, I know you {}!".format("John", "well") 16 | 17 | #### Exercise 18 | 19 | def who_do_you_know(): 20 | names = input("Enter the names of people you know, separated by commas: ") 21 | names_list = names.split(",") 22 | return names_list 23 | 24 | def ask_user(): 25 | # Ask user for their name 26 | # See if their name is in list of people 27 | # Print something if it is 28 | 29 | user_name = input("Enter your name: ") 30 | if user_name in who_do_you_know(): 31 | print("Hello {}, I know you!".format(user_name)) 32 | -------------------------------------------------------------------------------- /section2/5_list_comprehension.py: -------------------------------------------------------------------------------- 1 | my_list = [0, 1, 2, 3, 4] 2 | an_equal_list = [x for x in range(5)] 3 | 4 | for my_number in range(10): 5 | print(my_number) 6 | 7 | [my_number for my_number in range(10)] 8 | 9 | [my_number * 2 for my_number in range(10)] 10 | 11 | 1 % 2 12 | 2 % 2 13 | 5 % 2 14 | 8 % 3 15 | 16 | [n for n in range(10) if n % 2 == 0] 17 | 18 | names_list = ["John", "Rolf", "Anne"] 19 | lowercase_names = [name.lower() for name in names_list] 20 | print(lowercase_names) 21 | -------------------------------------------------------------------------------- /section2/6_dictionaries.py: -------------------------------------------------------------------------------- 1 | my_dict = { 2 | 'name': 'Jose', 3 | 'location': 'UK' 4 | } 5 | 6 | lottery_player = { 7 | 'name': 'Rolf', 8 | 'numbers': (13, 22, 3, 6, 9) 9 | } 10 | 11 | dict_in_dict = { 12 | 'universities': [ 13 | { 14 | 'name': 'Oxford', 15 | 'location': 'UK' 16 | }, 17 | { 18 | 'name': 'Harvard', 19 | 'location': 'US' 20 | } 21 | ] 22 | } 23 | 24 | ## 25 | 26 | lottery_player = { 27 | 'name': 'Rolf', 28 | 'numbers': (13, 22, 3, 6, 9) 29 | } 30 | 31 | players = [ 32 | { 33 | 'name': 'Rolf', 34 | 'numbers': (13, 22, 3, 6, 9) 35 | }, 36 | { 37 | 'name': 'John', 38 | 'numbers': (22, 3, 5, 7, 9) 39 | } 40 | ] 41 | 42 | # How could we select one of these? 43 | 44 | player = players[0] 45 | 46 | # How could we add all the numbers of a player? 47 | 48 | sum(player['numbers']) 49 | 50 | # We have a method that takes in a list—it does not have to be a list of numbers 51 | # of a player. Indeed, we could do something like this: 52 | 53 | sum([1, 2, 3, 4, 5]) 54 | 55 | # Wouldn't it be nice if the player itself (the dictionary) had a method 56 | # that would give us the sum of its numbers? Something like this: 57 | 58 | player.total() 59 | 60 | # If the player had a method that gives us the sum of its numbers, 61 | # it makes it more difficult to "game" the system—we can no longer pass in 62 | # a different list of numbers. 63 | 64 | # In addition, because what we are interested in is the sum of the players' numbers, 65 | # it makes sense for the player itself to tell us that, and not some other method 66 | # that is not a part of the player. 67 | -------------------------------------------------------------------------------- /section2/7_classes_objects.py: -------------------------------------------------------------------------------- 1 | my_list_variable = [1, 2, 3] 2 | print(my_list_variable) 3 | 4 | another_list = my_list_variable 5 | another_list.append(4) 6 | print(another_list) 7 | print(my_list_variable) 8 | 9 | ### 10 | 11 | final_list = [n for n in my_list_variable] 12 | final_list.append(5) 13 | print(final_list) 14 | print(my_list_variable) 15 | 16 | ### 17 | 18 | class Student: 19 | def __init__(self): 20 | self.name = "John" 21 | self.school = "Harvard" 22 | 23 | my_student_variable = Student() 24 | print(my_student_variable) 25 | print(my_student_variable.name) 26 | print(my_student_variable.school) 27 | 28 | class Student: 29 | def __init__(self, name, school): 30 | self.name = name 31 | self.school = school 32 | 33 | Student() 34 | another_student = Student("Rolf", "MIT") 35 | print(another_student) 36 | print(another_student.name) 37 | print(another_student.school) 38 | 39 | class Student: 40 | def __init__(self, name, school): 41 | self.name = name 42 | self.school = school 43 | self.marks = [] 44 | 45 | anna = Student("Anna", "Oxford") 46 | print(anna.marks) 47 | anna.marks.append(56) 48 | anna.marks.append(99) 49 | print(anna.marks) 50 | 51 | class Student: 52 | def __init__(self, name, school): 53 | self.name = name 54 | self.school = school 55 | self.marks = [] 56 | 57 | def average(self): 58 | return sum(marks) / len(marks) 59 | 60 | 61 | anna = Student("Anna", "Oxford") 62 | print(anna.marks) 63 | anna.marks.append(56) 64 | anna.marks.append(99) 65 | print(anna.marks) 66 | print(anna.average()) 67 | -------------------------------------------------------------------------------- /section2/8_static_class_methods.py: -------------------------------------------------------------------------------- 1 | 2 | class Student: 3 | def __init__(self, name, school): 4 | self.name = name 5 | self.school = school 6 | self.marks = [] 7 | 8 | def average(self): 9 | return sum(marks) / len(marks) 10 | 11 | def go_to_school(self): 12 | return "I'm going to {}".format(self.school) 13 | 14 | anna = Student("Anna", "Oxford") 15 | rolf = Student("Rolf", "Harvard") 16 | 17 | print(anna.go_to_school()) 18 | print(rolf.go_to_school()) 19 | 20 | ### 21 | 22 | class Student: 23 | def __init__(self, name, school): 24 | self.name = name 25 | self.school = school 26 | self.marks = [] 27 | 28 | def average(self): 29 | return sum(marks) / len(marks) 30 | 31 | def go_to_school(self): 32 | return "I'm going to school" 33 | 34 | anna = Student("Anna", "Oxford") 35 | rolf = Student("Rolf", "Harvard") 36 | 37 | print(anna.go_to_school()) 38 | print(rolf.go_to_school()) 39 | 40 | ### 41 | 42 | class Student: 43 | def __init__(self, name, school): 44 | self.name = name 45 | self.school = school 46 | self.marks = [] 47 | 48 | def average(self): 49 | return sum(marks) / len(marks) 50 | 51 | @staticmethod 52 | def go_to_school(): 53 | return "I'm going to school" 54 | 55 | anna = Student("Anna", "Oxford") 56 | rolf = Student("Rolf", "Harvard") 57 | 58 | print(anna.go_to_school()) 59 | print(rolf.go_to_school()) 60 | 61 | ### 62 | 63 | class Student: 64 | def __init__(self, name, school): 65 | self.name = name 66 | self.school = school 67 | self.marks = [] 68 | 69 | def average(self): 70 | return sum(marks) / len(marks) 71 | 72 | def friend(self, friend_name): 73 | return Student(friend_name, self.school) 74 | 75 | anna = Student("Anna", "Oxford") 76 | 77 | friend = anna.friend("Greg") 78 | print(friend.name) 79 | print(friend.school) 80 | -------------------------------------------------------------------------------- /section2/9_inheritance.py: -------------------------------------------------------------------------------- 1 | class Student: 2 | def __init__(self, name, school): 3 | self.name = name 4 | self.school = school 5 | self.marks = [] 6 | 7 | def average(self): 8 | return sum(marks) / len(marks) 9 | 10 | def friend(self, friend_name): 11 | return Student(friend_name, self.school) 12 | 13 | anna = Student("Anna", "Oxford") 14 | 15 | friend = anna.friend("Greg") 16 | print(friend.name) 17 | print(friend.school) 18 | 19 | ### 20 | 21 | class WorkingStudent(Student): 22 | def __init__(self, name, school, salary): 23 | super().__init__(name, school) 24 | self.salary = salary 25 | 26 | rolf = WorkingStudent("Rolf", "Harvard", 20.00) 27 | sue = rolf.friend("Sue") 28 | print(sue.salary) # Error! 29 | 30 | ### 31 | 32 | class Student: 33 | def __init__(self, name, school): 34 | self.name = name 35 | self.school = school 36 | self.marks = [] 37 | 38 | def average(self): 39 | return sum(marks) / len(marks) 40 | 41 | @classmethod 42 | def friend(cls, origin, friend_name, *args): 43 | return cls(friend_name, origin.school, *args) 44 | 45 | class WorkingStudent(Student): 46 | def __init__(self, name, school, salary): 47 | super().__init__(name, school) 48 | self.salary = salary 49 | 50 | rolf = WorkingStudent("Rolf", "Harvard", 20.00) 51 | sue = WorkingStudent.friend(rolf, "Sue", 15.00) 52 | print(sue.salary) # This works! 53 | -------------------------------------------------------------------------------- /section3/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask,jsonify,request,render_template 2 | 3 | app = Flask(__name__) 4 | 5 | stores = [{ 6 | 'name': 'My Store', 7 | 'items': [{'name':'my item', 'price': 15.99 }] 8 | }] 9 | 10 | @app.route('/') 11 | def home(): 12 | return render_template('index.html') 13 | 14 | #post /store data: {name :} 15 | @app.route('/store' , methods=['POST']) 16 | def create_store(): 17 | request_data = request.get_json() 18 | new_store = { 19 | 'name':request_data['name'], 20 | 'items':[] 21 | } 22 | stores.append(new_store) 23 | return jsonify(new_store) 24 | #pass 25 | 26 | #get /store/ data: {name :} 27 | @app.route('/store/') 28 | def get_store(name): 29 | for store in stores: 30 | if store['name'] == name: 31 | return jsonify(store) 32 | return jsonify ({'message': 'store not found'}) 33 | #pass 34 | 35 | #get /store 36 | @app.route('/store') 37 | def get_stores(): 38 | return jsonify({'stores': stores}) 39 | #pass 40 | 41 | #post /store/ data: {name :} 42 | @app.route('/store//item' , methods=['POST']) 43 | def create_item_in_store(name): 44 | request_data = request.get_json() 45 | for store in stores: 46 | if store['name'] == name: 47 | new_item = { 48 | 'name': request_data['name'], 49 | 'price': request_data['price'] 50 | } 51 | store['items'].append(new_item) 52 | return jsonify(new_item) 53 | return jsonify ({'message' :'store not found'}) 54 | #pass 55 | 56 | #get /store//item data: {name :} 57 | @app.route('/store//item') 58 | def get_item_in_store(name): 59 | for store in stores: 60 | if store['name'] == name: 61 | return jsonify( {'items':store['items'] } ) 62 | return jsonify ({'message':'store not found'}) 63 | 64 | #pass 65 | 66 | app.run(port=5000) 67 | -------------------------------------------------------------------------------- /section3/templates/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 19 | 20 | 21 | 22 |
23 | Hello, world! 24 |
25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /section4/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask_restful import Resource, Api, reqparse 3 | from flask_jwt import JWT, jwt_required, current_identity 4 | 5 | from security import authenticate, identity 6 | 7 | app = Flask(__name__) 8 | app.config['PROPAGATE_EXCEPTIONS'] = True # To allow flask propagating exception even if debug is set to false on app 9 | app.secret_key = 'jose' 10 | api = Api(app) 11 | 12 | jwt = JWT(app, authenticate, identity) 13 | 14 | items = [] 15 | 16 | class Item(Resource): 17 | parser = reqparse.RequestParser() 18 | parser.add_argument('price', 19 | type=float, 20 | required=True, 21 | help="This field cannot be left blank!" 22 | ) 23 | 24 | @jwt_required() 25 | def get(self, name): 26 | return {'item': next(filter(lambda x: x['name'] == name, items), None)} 27 | 28 | def post(self, name): 29 | if next(filter(lambda x: x['name'] == name, items), None) is not None: 30 | return {'message': "An item with name '{}' already exists.".format(name)} 31 | 32 | data = Item.parser.parse_args() 33 | 34 | item = {'name': name, 'price': data['price']} 35 | items.append(item) 36 | return item 37 | 38 | @jwt_required() 39 | def delete(self, name): 40 | global items 41 | items = list(filter(lambda x: x['name'] != name, items)) 42 | return {'message': 'Item deleted'} 43 | 44 | @jwt_required() 45 | def put(self, name): 46 | data = Item.parser.parse_args() 47 | # Once again, print something not in the args to verify everything works 48 | item = next(filter(lambda x: x['name'] == name, items), None) 49 | if item is None: 50 | item = {'name': name, 'price': data['price']} 51 | items.append(item) 52 | else: 53 | item.update(data) 54 | return item 55 | 56 | class ItemList(Resource): 57 | def get(self): 58 | return {'items': items} 59 | 60 | api.add_resource(Item, '/item/') 61 | api.add_resource(ItemList, '/items') 62 | 63 | if __name__ == '__main__': 64 | app.run(debug=True) # important to mention debug=True 65 | -------------------------------------------------------------------------------- /section4/security.py: -------------------------------------------------------------------------------- 1 | from werkzeug.security import safe_str_cmp 2 | from user import User 3 | 4 | users = [ 5 | User(1, 'user1', 'abcxyz'), 6 | User(2, 'user2', 'abcxyz'), 7 | ] 8 | 9 | username_table = {u.username: u for u in users} 10 | userid_table = {u.id: u for u in users} 11 | 12 | def authenticate(username, password): 13 | user = username_table.get(username, None) 14 | if user and safe_str_cmp(user.password, password): 15 | return user 16 | 17 | def identity(payload): 18 | user_id = payload['identity'] 19 | return userid_table.get(user_id, None) 20 | -------------------------------------------------------------------------------- /section4/user.py: -------------------------------------------------------------------------------- 1 | class User(object): 2 | def __init__(self, id, username, password): 3 | self.id = id 4 | self.username = username 5 | self.password = password 6 | -------------------------------------------------------------------------------- /section5/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask, request 2 | from flask_restful import Api 3 | from flask_jwt import JWT 4 | 5 | from security import authenticate, identity 6 | from user import UserRegister 7 | from item import Item, ItemList 8 | 9 | app = Flask(__name__) 10 | app.secret_key = 'jose' 11 | api = Api(app) 12 | 13 | jwt = JWT(app, authenticate, identity) 14 | 15 | api.add_resource(Item, '/item/') 16 | api.add_resource(ItemList, '/items') 17 | api.add_resource(UserRegister, '/register') 18 | 19 | if __name__ == '__main__': 20 | app.run(debug=True) # important to mention debug=True 21 | -------------------------------------------------------------------------------- /section5/create_table.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | 3 | connection = sqlite3.connect('data.db') 4 | 5 | cursor = connection.cursor() 6 | 7 | # MUST BE INTEGER 8 | # This is the only place where int vs INTEGER matters—in auto-incrementing columns 9 | create_table = "CREATE TABLE IF NOT EXISTS users (id INTEGER PRIMARY KEY, username text, password text)" 10 | cursor.execute(create_table) 11 | 12 | create_table = "CREATE TABLE IF NOT EXISTS items (name text PRIMARY KEY, price real)" 13 | cursor.execute(create_table) 14 | 15 | connection.commit() 16 | 17 | connection.close() 18 | -------------------------------------------------------------------------------- /section5/item.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from flask_jwt import jwt_required 3 | import sqlite3 4 | 5 | class Item(Resource): 6 | TABLE_NAME = 'items' 7 | 8 | parser = reqparse.RequestParser() 9 | parser.add_argument('price', 10 | type=float, 11 | required=True, 12 | help="This field cannot be left blank!" 13 | ) 14 | 15 | @jwt_required() 16 | def get(self, name): 17 | item = self.find_by_name(name) 18 | if item: 19 | return item 20 | return {'message': 'Item not found'}, 404 21 | 22 | @classmethod 23 | def find_by_name(cls, name): 24 | connection = sqlite3.connect('data.db') 25 | cursor = connection.cursor() 26 | 27 | query = "SELECT * FROM {table} WHERE name=?".format(table=cls.TABLE_NAME) 28 | result = cursor.execute(query, (name,)) 29 | row = result.fetchone() 30 | connection.close() 31 | 32 | if row: 33 | return {'item': {'name': row[0], 'price': row[1]}} 34 | 35 | def post(self, name): 36 | if self.find_by_name(name): 37 | return {'message': "An item with name '{}' already exists.".format(name)} 38 | 39 | data = Item.parser.parse_args() 40 | 41 | item = {'name': name, 'price': data['price']} 42 | 43 | try: 44 | Item.insert(item) 45 | except: 46 | return {"message": "An error occurred inserting the item."} 47 | 48 | return item 49 | 50 | @classmethod 51 | def insert(cls, item): 52 | connection = sqlite3.connect('data.db') 53 | cursor = connection.cursor() 54 | 55 | query = "INSERT INTO {table} VALUES(?, ?)".format(table=cls.TABLE_NAME) 56 | cursor.execute(query, (item['name'], item['price'])) 57 | 58 | connection.commit() 59 | connection.close() 60 | 61 | @jwt_required() 62 | def delete(self, name): 63 | connection = sqlite3.connect('data.db') 64 | cursor = connection.cursor() 65 | 66 | query = "DELETE FROM {table} WHERE name=?".format(table=self.TABLE_NAME) 67 | cursor.execute(query, (name,)) 68 | 69 | connection.commit() 70 | connection.close() 71 | 72 | return {'message': 'Item deleted'} 73 | 74 | @jwt_required() 75 | def put(self, name): 76 | data = Item.parser.parse_args() 77 | item = self.find_by_name(name) 78 | updated_item = {'name': name, 'price': data['price']} 79 | if item is None: 80 | try: 81 | Item.insert(updated_item) 82 | except: 83 | return {"message": "An error occurred inserting the item."} 84 | else: 85 | try: 86 | Item.update(updated_item) 87 | except: 88 | raise 89 | return {"message": "An error occurred updating the item."} 90 | return updated_item 91 | 92 | @classmethod 93 | def update(cls, item): 94 | connection = sqlite3.connect('data.db') 95 | cursor = connection.cursor() 96 | 97 | query = "UPDATE {table} SET price=? WHERE name=?".format(table=cls.TABLE_NAME) 98 | cursor.execute(query, (item['price'], item['name'])) 99 | 100 | connection.commit() 101 | connection.close() 102 | 103 | 104 | class ItemList(Resource): 105 | TABLE_NAME = 'items' 106 | 107 | def get(self): 108 | connection = sqlite3.connect('data.db') 109 | cursor = connection.cursor() 110 | 111 | query = "SELECT * FROM {table}".format(table=self.TABLE_NAME) 112 | result = cursor.execute(query) 113 | items = [] 114 | for row in result: 115 | items.append({'name': row[0], 'price': row[1]}) 116 | connection.close() 117 | 118 | return {'items': items} 119 | -------------------------------------------------------------------------------- /section5/security.py: -------------------------------------------------------------------------------- 1 | from werkzeug.security import safe_str_cmp 2 | from user import User 3 | 4 | def authenticate(username, password): 5 | user = User.find_by_username(username) 6 | if user and safe_str_cmp(user.password, password): 7 | return user 8 | 9 | def identity(payload): 10 | user_id = payload['identity'] 11 | return User.find_by_id(user_id) 12 | -------------------------------------------------------------------------------- /section5/user.py: -------------------------------------------------------------------------------- 1 | import sqlite3 2 | from flask_restful import Resource, reqparse 3 | 4 | class User(Resource): 5 | TABLE_NAME = 'users' 6 | 7 | def __init__(self, _id, username, password): 8 | self.id = _id 9 | self.username = username 10 | self.password = password 11 | 12 | @classmethod 13 | def find_by_username(cls, username): 14 | connection = sqlite3.connect('data.db') 15 | cursor = connection.cursor() 16 | 17 | query = "SELECT * FROM {table} WHERE username=?".format(table=cls.TABLE_NAME) 18 | result = cursor.execute(query, (username,)) 19 | row = result.fetchone() 20 | if row: 21 | user = cls(*row) 22 | else: 23 | user = None 24 | 25 | connection.close() 26 | return user 27 | 28 | @classmethod 29 | def find_by_id(cls, _id): 30 | connection = sqlite3.connect('data.db') 31 | cursor = connection.cursor() 32 | 33 | query = "SELECT * FROM {table} WHERE id=?".format(table=cls.TABLE_NAME) 34 | result = cursor.execute(query, (_id,)) 35 | row = result.fetchone() 36 | if row: 37 | user = cls(*row) 38 | else: 39 | user = None 40 | 41 | connection.close() 42 | return user 43 | 44 | 45 | class UserRegister(Resource): 46 | TABLE_NAME = 'users' 47 | 48 | parser = reqparse.RequestParser() 49 | parser.add_argument('username', 50 | type=str, 51 | required=True, 52 | help="This field cannot be left blank!" 53 | ) 54 | parser.add_argument('password', 55 | type=str, 56 | required=True, 57 | help="This field cannot be left blank!" 58 | ) 59 | 60 | def post(self): 61 | data = UserRegister.parser.parse_args() 62 | 63 | if User.find_by_username(data['username']): 64 | return {"message": "User with that username already exists."}, 400 65 | 66 | connection = sqlite3.connect('data.db') 67 | cursor = connection.cursor() 68 | 69 | query = "INSERT INTO {table} VALUES (NULL, ?, ?)".format(table=self.TABLE_NAME) 70 | cursor.execute(query, (data['username'], data['password'])) 71 | 72 | connection.commit() 73 | connection.close() 74 | 75 | return {"message": "User created successfully."}, 201 76 | -------------------------------------------------------------------------------- /section6/app.py: -------------------------------------------------------------------------------- 1 | from flask import Flask 2 | from flask_restful import Api 3 | from flask_jwt import JWT 4 | 5 | from security import authenticate, identity 6 | from resources.user import UserRegister 7 | from resources.item import Item, ItemList 8 | from resources.store import Store, StoreList 9 | 10 | app = Flask(__name__) 11 | app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///data.db' 12 | app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False 13 | app.secret_key = 'EiEiO' 14 | api = Api(app) 15 | 16 | @app.before_first_request 17 | def create_tables(): 18 | db.create_all() 19 | 20 | jwt = JWT(app, authenticate, identity) # /auth 21 | 22 | api.add_resource(Store, '/store/') 23 | api.add_resource(StoreList, '/stores') 24 | api.add_resource(Item, '/item/') 25 | api.add_resource(ItemList, '/items') 26 | api.add_resource(UserRegister, '/register') 27 | 28 | if __name__ == '__main__': 29 | from db import db 30 | db.init_app(app) 31 | app.run(port=5000, debug=True) 32 | -------------------------------------------------------------------------------- /section6/db.py: -------------------------------------------------------------------------------- 1 | from flask_sqlalchemy import SQLAlchemy 2 | 3 | db = SQLAlchemy() 4 | -------------------------------------------------------------------------------- /section6/models/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /section6/models/item.py: -------------------------------------------------------------------------------- 1 | from db import db 2 | 3 | class ItemModel(db.Model): 4 | __tablename__ = 'items' 5 | 6 | id = db.Column(db.Integer, primary_key=True) 7 | name = db.Column(db.String(80)) 8 | price = db.Column(db.Float(precision=2)) 9 | 10 | store_id = db.Column(db.Integer, db.ForeignKey('stores.id')) 11 | store = db.relationship('StoreModel') 12 | 13 | def __init__(self, name, price, store_id): 14 | self.name = name 15 | self.price = price 16 | self.store_id = store_id 17 | 18 | def json(self): 19 | return {'name': self.name, 'price': self.price} 20 | 21 | @classmethod 22 | def find_by_name(cls, name): 23 | return cls.query.filter_by(name=name).first() 24 | 25 | def save_to_db(self): 26 | db.session.add(self) 27 | db.session.commit() 28 | 29 | def delete_from_db(self): 30 | db.session.delete(self) 31 | db.session.commit() 32 | -------------------------------------------------------------------------------- /section6/models/store.py: -------------------------------------------------------------------------------- 1 | from db import db 2 | 3 | class StoreModel(db.Model): 4 | __tablename__ = 'stores' 5 | 6 | id = db.Column(db.Integer, primary_key=True) 7 | name = db.Column(db.String(80)) 8 | 9 | items = db.relationship('ItemModel', lazy='dynamic') 10 | 11 | def __init__(self, name): 12 | self.name = name 13 | 14 | def json(self): 15 | return {'name': self.name, 'items': [item.json() for item in self.items.all()]} 16 | 17 | @classmethod 18 | def find_by_name(cls, name): 19 | return cls.query.filter_by(name=name).first() 20 | 21 | def save_to_db(self): 22 | db.session.add(self) 23 | db.session.commit() 24 | 25 | def delete_from_db(self): 26 | db.session.delete(self) 27 | db.session.commit() 28 | -------------------------------------------------------------------------------- /section6/models/user.py: -------------------------------------------------------------------------------- 1 | from db import db 2 | 3 | class UserModel(db.Model): 4 | __tablename__ = 'users' 5 | 6 | id = db.Column(db.Integer, primary_key=True) 7 | username = db.Column(db.String(80)) 8 | password = db.Column(db.String(80)) 9 | 10 | def __init__(self, username, password): 11 | self.username = username 12 | self.password = password 13 | 14 | def save_to_db(self): 15 | db.session.add(self) 16 | db.session.commit() 17 | 18 | @classmethod 19 | def find_by_username(cls, username): 20 | return cls.query.filter_by(username=username).first() 21 | 22 | @classmethod 23 | def find_by_id(cls, _id): 24 | return cls.query.filter_by(id=_id).first() 25 | -------------------------------------------------------------------------------- /section6/resources/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /section6/resources/item.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from flask_jwt import jwt_required 3 | from models.item import ItemModel 4 | 5 | class Item(Resource): 6 | parser = reqparse.RequestParser() 7 | parser.add_argument('price', 8 | type=float, 9 | required=True, 10 | help="This field cannot be left blank!" 11 | ) 12 | parser.add_argument('store_id', 13 | type=int, 14 | required=True, 15 | help="Every item needs a store_id." 16 | ) 17 | 18 | @jwt_required() 19 | def get(self, name): 20 | item = ItemModel.find_by_name(name) 21 | if item: 22 | return item.json() 23 | return {'message': 'Item not found'}, 404 24 | 25 | def post(self, name): 26 | if ItemModel.find_by_name(name): 27 | return {'message': "An item with name '{}' already exists.".format(name)}, 400 28 | 29 | data = Item.parser.parse_args() 30 | 31 | item = ItemModel(name, data['price'], data['store_id']) 32 | 33 | try: 34 | item.save_to_db() 35 | except: 36 | return {"message": "An error occurred inserting the item."}, 500 37 | 38 | return item.json(), 201 39 | 40 | def delete(self, name): 41 | item = ItemModel.find_by_name(name) 42 | if item: 43 | item.delete_from_db() 44 | 45 | return {'message': 'Item deleted'} 46 | 47 | def put(self, name): 48 | data = Item.parser.parse_args() 49 | 50 | item = ItemModel.find_by_name(name) 51 | 52 | if item: 53 | item.price = data['price'] 54 | else: 55 | item = ItemModel(name, data['price']) 56 | 57 | item.save_to_db() 58 | 59 | return item.json() 60 | 61 | class ItemList(Resource): 62 | def get(self): 63 | return {'items': list(map(lambda x: x.json(), ItemModel.query.all()))} 64 | -------------------------------------------------------------------------------- /section6/resources/store.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from models.store import StoreModel 3 | 4 | class Store(Resource): 5 | def get(self, name): 6 | store = StoreModel.find_by_name(name) 7 | if store: 8 | return store.json() 9 | return {'message': 'Store not found'}, 404 10 | 11 | def post(self, name): 12 | if StoreModel.find_by_name(name): 13 | return {'message': "A store with name '{}' already exists.".format(name)}, 400 14 | 15 | store = StoreModel(name) 16 | try: 17 | store.save_to_db() 18 | except: 19 | return {"message": "An error occurred creating the store."}, 500 20 | 21 | return store.json(), 201 22 | 23 | def delete(self, name): 24 | store = StoreModel.find_by_name(name) 25 | if store: 26 | store.delete_from_db() 27 | 28 | return {'message': 'Store deleted'} 29 | 30 | class StoreList(Resource): 31 | def get(self): 32 | return {'stores': list(map(lambda x: x.json(), StoreModel.query.all()))} 33 | -------------------------------------------------------------------------------- /section6/resources/user.py: -------------------------------------------------------------------------------- 1 | from flask_restful import Resource, reqparse 2 | from models.user import UserModel 3 | 4 | class UserRegister(Resource): 5 | 6 | parser = reqparse.RequestParser() 7 | parser.add_argument('username', 8 | type=str, 9 | required=True, 10 | help="This field cannot be blank." 11 | ) 12 | parser.add_argument('password', 13 | type=str, 14 | required=True, 15 | help="This field cannot be blank." 16 | ) 17 | 18 | def post(self): 19 | data = UserRegister.parser.parse_args() 20 | 21 | if UserModel.find_by_username(data['username']): 22 | return {"message": "A user with that username already exists"}, 400 23 | 24 | user = UserModel(data['username'], data['password']) 25 | user.save_to_db() 26 | 27 | return {"message": "User created successfully."}, 201 28 | -------------------------------------------------------------------------------- /section6/security.py: -------------------------------------------------------------------------------- 1 | from werkzeug.security import safe_str_cmp 2 | from models.user import UserModel 3 | 4 | def authenticate(username, password): 5 | user = UserModel.find_by_username(username) 6 | if user and safe_str_cmp(user.password, password): 7 | return user 8 | 9 | def identity(payload): 10 | user_id = payload['identity'] 11 | return UserModel.find_by_id(user_id) 12 | --------------------------------------------------------------------------------