Skip to content

Days 9-12 API Method: PUT Issue #21

Closed
@gloc-mike

Description

@gloc-mike

I am working through "Days 9-12 API Star" in the 100 Days of Web Python course and I am having issues with the PUT method.

I have cross checked my code against the GitHub version and my code looks logically the same but when I test the PUT method using Postman the 'update_car' function is not called/executed.

I have added print statements to the 'update_car' function to confirm this and indeed there is nothing printed in the console. Adding a print to the 'get_car' function however does print to the console.

My sequence of testing is the some as the video i.e. submit a GET request from Postman (for id 55); the submit the PUT request with updated parameters in the body of the request.

I have created a Git of the server output: https://gist.github.com/gloc-mike/03d1b7eda91b3a07ced3c6efa4ce4d34

In that you can see that the GET request was submitted and the print statement shows id 55; then when I submit the PUT request, the server responds with a 302 (redirect?), does not print my debug messages and then performs a GET request (system generated).

I'm on Mac; PyCharm (2019.3.1); virtual environments pipenv and pyenv; Pyton 3.7.4; apistar version is the recommended version. Also tried running it from the terminal (to remove PyCharm from the equation) but I get the same results.

There is a setting in Postman:

Automatically follow redirects
Prevent requests that return a 300-series response from being automatically redirected.

which I have tried turning off (default is on) but apart from that nothing else has been changed in Postman.

The PUT request from Postman:

PUT /55 HTTP/1.1
Host: 127.0.0.1:5000
User-Agent: PostmanRuntime/7.20.1
Accept: */*
Cache-Control: no-cache
Postman-Token: 914028c8-fc63-47f0-a99f-9132150dd2d3,80314209-e775-4466-8bbd-426400394684
Accept-Encoding: gzip, deflate
Referer: http://127.0.0.1:5000/55
Connection: keep-alive
cache-control: no-cache
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary7MA4YWxkTrZu0gW

Content-Disposition: form-data; name="manufacturer"

Jeep
------WebKitFormBoundary7MA4YWxkTrZu0gW--,
Content-Disposition: form-data; name="manufacturer"

Jeep
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Content-Disposition: form-data; name="model"

Mountain High II
------WebKitFormBoundary7MA4YWxkTrZu0gW--,
Content-Disposition: form-data; name="manufacturer"

Jeep
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Content-Disposition: form-data; name="model"

Mountain High II
------WebKitFormBoundary7MA4YWxkTrZu0gW--
Content-Disposition: form-data; name="year"

2001
------WebKitFormBoundary7MA4YWxkTrZu0gW--

My pipfile:

[[source]]
name = "pypi"
url = "https://pypi.org/simple"
verify_ssl = true

[dev-packages]

[packages]
pytest = "*"
apistar = ">=0.5.41,<0.5.1000"

[requires]
python_version = "3.7"

My version of app.py sans the delete method:

import json
from typing import List

from apistar import App, Route, types, validato
![Screen Shot 2019-12-20 at 18 40 08](https://user-images.githubusercontent.com/2324626/71239822-a5454d80-235b-11ea-9c70-7abdec867949.png)
![Screen Shot 2019-12-20 at 18 40 53](https://user-images.githubusercontent.com/2324626/71239826-ad04f200-235b-11ea-8c12-dcbf6ecf2201.png)

rs
from apistar.http import JSONResponse


# helpers
def _load_cars_data():
    with open('cars.json') as f:
        cars = json.loads(f.read())
        # create a dictionary of the form {car_id: car}
        return {car['id']: car for car in cars}


# To load in the cars call the cars helper function
cars_db = _load_cars_data()

# Some constants
VALID_MANUFACTURERS = set([car['manufacturer'] for car in cars_db.values()])
CAR_NOT_FOUND = 'Car not found!'


# Definition - using APIStar's types.Type to get access to validation &
# serialisation
class Car(types.Type):
    # No auto-incrementer available so assign ID in POST - hence the need to
    # use 'allow_null=True' in this code
    id = validators.Integer(allow_null=True)
    manufacturer = validators.String(enum=list(VALID_MANUFACTURERS))
    model = validators.String(max_length=35)
    year = validators.Integer(minimum=1900, maximum=2050)
    # Make something OPTIONAL just add "default=''"
    vin = validators.String(max_length=50, default='')


# API methods
def list_cars() -> List[Car]:
    # cars_db.items() returns key, value tuple so to get the value from the
    # tuple one must use index [1]
    # Wrapping 'car[1]' with Car() serialises it :)  i.e. turn a dictionary
    # into a Car object
    return [Car(car[1]) for car in sorted(cars_db.items())]


def create_car(car: Car) -> JSONResponse:
    # Since no auto-increment available, manually determine the length of the
    # DB and increment by 1
    car_id = max(cars_db.keys())+1
    # Update the ID for the car we want to create with the new ID
    car.id = car_id
    # Store the new car in the DB
    cars_db[car_id] = car
    return JSONResponse(Car(car), status_code=201)


def get_car(car_id: int) -> JSONResponse:
    print(f"GET car: {car_id}")
    car = cars_db.get(car_id)
    # if a car exists, ".get(car_id)" returns the car otherwise returns None
    if not car:
        error = {'error': CAR_NOT_FOUND}
        return JSONResponse(error, status_code=404)
    return JSONResponse(Car(car), status_code=200)


def update_car(car_id: int, car: Car) -> JSONResponse:
    print(f"PUT car: {car}")
    # Check to see if the car exists so it can be updated
    if not cars_db.get(car_id):
        error = {'error', CAR_NOT_FOUND}
        return JSONResponse(error, status_code=404)
    # So it's ok, go ahead and update the car
    car.id = car_id
    cars_db[car_id] = car
    print(f"PUT cars_db[car_id]: {cars_db[car_id]}")
    return JSONResponse(Car(car), status_code=200)


def delete_car(car_id: int) -> JSONResponse:
    pass

routes = [
    Route('/', method='GET', handler=list_cars),
    Route('/', method='POST', handler=create_car),
    Route('/{car_id}/', method='GET', handler=get_car),
    Route('/{car_id}/', method='PUT', handler=update_car),
    Route('/{car_id}/', method='DELETE', handler=delete_car),
]

app = App(routes=routes)

if __name__ == '__main__':
    app.serve('127.0.0.1', 5000, debug=True)

Any help with how I can trace this issue would be greatly appreciated.

Thanks
Michael

Metadata

Metadata

Assignees

Labels

No labels
No labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions