{
  "schema_version": 2,
  "id": "integrate/redisom-for-python",
  "title": "RedisOM for Python",
  "url": "https://redis.io/docs/latest/integrate/redisom-for-python/",
  "summary": "Learn how to build with Redis Stack and Python",
  "aliases": [
    "/connect/clients/om-clients/stack-python/",
    "/develop/connect/clients/om-clients/stack-python/"
  ],
  "tags": [
    "docs",
    "integrate",
    "oss",
    "rs",
    "rc"
  ],
  "last_updated": "2026-09-17T14:54:53+01:00",
  "children": [],
  "page_type": "content",
  "content_hash": "3ef7e558b914e179527bdac35aa06eee1d014a72528e213f4526115d3f1f4f35",
  "sections": [
    {
      "id": "overview-1",
      "title": "Overview",
      "role": "overview",
      "text": "[Redis OM Python](https://github.com/redis/redis-om-python) is a Redis client that provides high-level abstractions for managing document data in Redis. This tutorial shows you how to get up and running with Redis OM Python, Redis Stack, and the [Flask](https://flask.palletsprojects.com/) micro-framework.\n\nWe'd love to see what you build with Redis Stack and Redis OM.  [Join the Redis community on Discord](https://discord.gg/redis) to chat with us about all things Redis OM and Redis Stack.  Read more about Redis OM Python [our announcement blog post](https://redis.com/blog/introducing-redis-om-for-python/)."
    },
    {
      "id": "overview",
      "title": "Overview",
      "role": "overview",
      "text": "This application, an API built with Flask and a simple domain model, demonstrates common data manipulation patterns using Redis OM. \n\nOur entity is a Person, with the following JSON representation:\n\n[code example]\n\nWe'll let Redis OM handle generation of unique IDs, which it does using [ULIDs](https://github.com/ulid/spec).  Redis OM will also handle creation of unique Redis key names for us, as well as saving and retrieving entities from JSON documents stored in a Redis Stack database."
    },
    {
      "id": "getting-started",
      "title": "Getting Started",
      "role": "setup",
      "text": ""
    },
    {
      "id": "requirements",
      "title": "Requirements",
      "role": "content",
      "text": "To run this application you'll need:\n\n* [git](https://git-scm.com/download) - to clone the repo to your machine. \n* [Python 3.9 or higher](https://www.python.org/downloads/).\n* A [Redis Stack](https://redis.io) database, or Redis with the [Search and Query](https://redis.io/docs/latest/develop/ai/search-and-query) and [JSON](https://redis.io/docs/latest/develop/data-types/json) features installed. We've provided a `docker-compose.yml` for this. You can also [sign up for a free 30Mb database with Redis Cloud](https://redis.com/try-free/?utm_source=redisio&utm_medium=referral&utm_campaign=2023-09-try_free&utm_content=cu-redis_cloud_users) - be sure to check the Redis Stack option when creating your cloud database.\n* [curl](https://curl.se/), or [Postman](https://www.postman.com/) - to send HTTP requests to the application.  We'll provide examples using curl in this document.\n* Optional: [Redis Insight](https://redis.com/redis-enterprise/redis-insight/), a free data visualization and database management tool for Redis.  When downloading Redis Insight, be sure to select version 2.x or use the version that comes with Redis Stack."
    },
    {
      "id": "get-the-source-code",
      "title": "Get the Source Code",
      "role": "content",
      "text": "Clone the repository from GitHub:\n\n[code example]"
    },
    {
      "id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials",
      "title": "Start a Redis Stack Database, or Configure your Redis Cloud Credentials",
      "role": "content",
      "text": "Next, we'll get a Redis Stack database up and running.  If you're using Docker:\n\n[code example]\n\nIf you're using Redis Cloud, you'll need the hostname, port number, and password for your database.  Use these to set the `REDIS_OM_URL` environment variable like this:\n\n[code example]\n\n(This step is not required when working with Docker as the Docker container runs Redis on `localhost` port `6379` with no password, which is the default connection that Redis OM uses.)\n\nFor example if your Redis Cloud database is at port `9139` on host `enterprise.redis.com` and your password is `5uper53cret` then you'd set `REDIS_OM_URL` as follows:\n\n[code example]"
    },
    {
      "id": "create-a-python-virtual-environment-and-install-the-dependencies",
      "title": "Create a Python Virtual Environment and Install the Dependencies",
      "role": "content",
      "text": "Create a Python virtual environment, and install the project dependencies which are [Flask](https://pypi.org/project/Flask/), [Requests](https://pypi.org/project/requests/) (used only in the data loader script) and [Redis OM](https://pypi.org/project/redis-om/):\n\n[code example]"
    },
    {
      "id": "start-the-flask-application",
      "title": "Start the Flask Application",
      "role": "content",
      "text": "Let's start the Flask application in development mode, so that Flask will restart the server for you each time you save code changes in `app.py`:\n\n[code example]\n\nIf all goes well, you should see output similar to this:\n\n[code example]\n\nYou're now up and running, and ready to perform CRUD operations on data with Redis, Search and Query, JSON and Redis OM for Python! To make sure the server's running, point your browser at `http://127.0.0.1:5000/`, where you can expect to see the application's basic home page:\n\n![screenshot](./images/python_server_running.png)"
    },
    {
      "id": "load-the-sample-data",
      "title": "Load the Sample Data",
      "role": "content",
      "text": "We've provided a small amount of sample data (it's in `data/people.json`.  The Python script `dataloader.py` loads each person into Redis by posting the data to the application's create a new person endpoint.  Run it like this:\n\n[code example]\n\nMake sure to take a copy of the output of the data loader, as your IDs will differ from those used in the tutorial.  To follow along, substitute your IDs for the ones shown above.  e.g. whenever we are working with Kareem Khan, change `01FX8RMR7T60ANQTS4P9NKPKX8` for the ID that your data loader assigned to Kareem in your Redis database."
    },
    {
      "id": "problems",
      "title": "Problems?",
      "role": "content",
      "text": "If the Flask server fails to start, take a look at its output.  If you see log entries similar to this:\n\n[code example]\n\nthen you need to start the Redis Docker container if using Docker, or set the `REDIS_OM_URL` environment variable if using Redis Cloud.\n\nIf you've set the `REDIS_OM_URL` environment variable, and the code errors with something like this on startup:\n\n[code example]\n\nthen you'll need to check that you used the correct hostname, port, password and format when setting `REDIS_OM_URL`.\n\nIf the data loader fails to post the sample data into the application, make sure that the Flask application is running **before** running the data loader."
    },
    {
      "id": "create-read-update-and-delete-data",
      "title": "Create, Read, Update and Delete Data",
      "role": "content",
      "text": "Let's create and manipulate some instances of our data model in Redis.  Here we'll look at how to call the Flask API with curl (you could also use Postman), how the code works, and how the data's stored in Redis."
    },
    {
      "id": "building-a-person-model-with-redis-om",
      "title": "Building a Person Model with Redis OM",
      "role": "content",
      "text": "Redis OM allows us to model entities using Python classes, and the [Pydantic](https://pypi.org/project/pydantic/) framework.  Our person model is contained in the file `person.py`.  Here's some notes about how it works:\n\n* We declare a class `Person` which extends a Redis OM class `JsonModel`.  This tells Redis OM that we want to store these entities in Redis as JSON documents.\n* We then declare each field in our model, specifying the data type and whether or not we want to index on that field.  For example, here's the `age` field, which we've declared as a positive integer that we want to index on:\n\n[code example]\n\n* The `skills` field is a list of strings, declared thus:\n\n[code example]\n\n* For the `personal_statement` field, we don't want to index on the field's value, as it's a free text sentence rather than a single word or digit. For this, we'll tell Redis OM that we want to be able to perform full text searches on the values:\n\n[code example]\n\n* `address` works differently from the other fields.  Note that in our JSON representation of the model, address is an object rather than a string or numerical field.  With Redis OM, this is modeled as a second class, which extends the Redis OM `EmbeddedJsonModel` class:\n\n[code example]\n\n* Fields in an `EmbeddedJsonModel` are defined in the same way, so our class contains a field definition for each data item in the address.\n\n* Not every field in our JSON is present in every address, Redis OM allows us to declare a field as optional so long as we don't index it:\n\n[code example]\n\n* We can also set a default value for a field... let's say country should be \"United Kingdom\" unless otherwise specified:\n\n[code example]\n\n* Finally, to add the embedded address object to our Person model, we declare a field of type `Address` in the Person class:\n\n[code example]"
    },
    {
      "id": "adding-new-people",
      "title": "Adding New People",
      "role": "content",
      "text": "The function `create_person` in `app.py` handles the creation of a new person in Redis.  It expects a JSON object that adheres to our Person model's schema.  The code to then create a new Person object with that data and save it in Redis is simple:\n\n[code example]\n\nWhen a new Person instance is created, Redis OM assigns it a unique ULID primary key, which we can access as `.pk`.  We return that to the caller, so that they know the ID of the object they just created.\n\nPersisting the object to Redis is then simply a matter of calling `.save()` on it.\n\nTry it out... with the server running, add a new person using curl:\n\n[code example]\n\nRunning the above curl command will return the unique ULID ID assigned to the newly created person. For example `01FX8SSSDN7PT9T3N0JZZA758G`."
    },
    {
      "id": "examining-the-data-in-redis",
      "title": "Examining the data in Redis",
      "role": "content",
      "text": "Let's take a look at what we just saved in Redis.  Using Redis Insight or redis-cli, connect to the database and look at the value stored at key `:person.Person:01FX8SSSDN7PT9T3N0JZZA758G`.  This is stored as a JSON document in Redis, so if using redis-cli you'll need the following command:\n\n[code example]\n\nIf you're using Redis Insight, the browser will render the key value for you when you click on the key name:\n\n![Data in Redis Insight](./images/python_insight_explore_person.png)\n\nWhen storing data as JSON in Redis, we can update and retrieve the whole document, or just parts of it.  For example, to retrieve only the person's address and first skill, use the following command (Redis Insight users should use the built in redis-cli for this):\n\n[code example]\n\nFor more information on the JSON Path syntax used to query JSON documents in Redis, see the [documentation](https://redis.io/docs/latest/develop/data-types/json/path#jsonpath-syntax)."
    },
    {
      "id": "find-a-person-by-id",
      "title": "Find a Person by ID",
      "role": "content",
      "text": "If we know a person's ID, we can retrieve their data.  The function `find_by_id` in `app.py` receives an ID as its parameter, and asks Redis OM to retrieve and populate a Person object using the ID and the Person `.get` class method:\n\n[code example]\n\nThe `.dict()` method converts our Person object to a Python dictionary that Flask then returns to the caller.\n\nNote that if there is no Person with the supplied ID in Redis, `get` will throw a `NotFoundError`.\n\nTry this out with curl, substituting `01FX8SSSDN7PT9T3N0JZZA758G` for the ID of a person that you just created in your database:\n\n[code example]\n\nThe server responds with a JSON object containing the user's data:\n\n[code example]"
    },
    {
      "id": "find-people-with-matching-first-and-last-name",
      "title": "Find People with Matching First and Last Name",
      "role": "content",
      "text": "Let's find all the people who have a given first and last name...  This is handled by the function `find_by_name` in `app.py`.\n\nHere, we're using Person's `find` class method that's provided by Redis OM.  We pass it a search query, specifying that we want to find people whose `first_name` field contains the value of the `first_name` parameter passed to `find_by_name` AND whose `last_name` field contains the value of the `last_name` parameter:\n\n[code example]\n\n`.all()` tells Redis OM that we want to retrieve all matching people.\n\nTry this out with curl as follows:\n\n[code example]\n\n**Note:** First and last name are case sensitive.\n\nThe server responds with an object containing `results`, an array of matches:\n\n[code example]"
    },
    {
      "id": "find-people-within-a-given-age-range",
      "title": "Find People within a Given Age Range",
      "role": "content",
      "text": "It's useful to be able to find people that fall into a given age range... the function `find_in_age_range` in `app.py` handles this as follows...\n\nWe'll again use Person's `find` class method, this time passing it a minimum and maximum age, specifying that we want results where the `age` field is between those values only:\n\n[code example]\n\nNote that we can also use `.sort_by` to specify which field we want our results sorted by.\n\nLet's find everyone between 30 and 47 years old, sorted by age:\n\n[code example]\n\nThis returns a `results` object containing an array of matches:\n\n[code example]"
    },
    {
      "id": "find-people-in-a-given-city-with-a-specific-skill",
      "title": "Find People in a Given City with a Specific Skill",
      "role": "content",
      "text": "Now, we'll try a slightly different sort of query.  We want to find all of the people that live in a given city AND who also have a certain skill.  This requires a search over both the `city` field which is a string, and the `skills` field, which is an array of strings.\n\nEssentially we want to say \"Find me all the people whose city is `city` AND whose skills array CONTAINS `desired_skill`\", where `city` and `desired_skill` are the parameters to the `find_matching_skill` function in `app.py`.  Here's the code for that:\n\n[code example]\n\nThe `<<` operator here is used to indicate \"in\" or \"contains\".\n\nLet's find all the guitar players in Sheffield:\n\n[code example]\n\n**Note:** `Sheffield` is case sensitive.\n\nThe server returns a `results` array containing matching people:\n\n[code example]"
    },
    {
      "id": "find-people-using-full-text-search-on-their-personal-statements",
      "title": "Find People using Full Text Search on their Personal Statements",
      "role": "content",
      "text": "Each person has a `personal_statement` field, which is a free text string containing a couple of sentences about them.  We chose to index this in a way that makes it full text searchable, so let's see how to use this now.  The code for this is in the function `find_matching_statements` in `app.py`.\n\nTo search for people who have the value of the parameter `search_term` in their `personal_statement` field, we use the `%` operator:\n\n[code example]\n\nLet's find everyone who talks about \"play\" in their personal statement.\n\n[code example]\n\nThe server responds with a `results` array of matching people:\n\n[code example]\n\nNote that we get results including matches for \"play\", \"plays\" and \"playing\"."
    },
    {
      "id": "update-a-persons-age",
      "title": "Update a Person's Age",
      "role": "content",
      "text": "As well as retrieving information from Redis, we'll also want to update a Person's data from time to time.  Let's see how to do that with Redis OM for Python.\n\nThe function `update_age` in `app.py` accepts two parameters: `id` and `new_age`.  Using these, we first retrieve the person's data from Redis and create a new object with it:\n\n[code example]\n\nAssuming we find the person, let's update their age and save the data back to Redis:\n\n[code example]\n\nLet's change Kareem Khan's age from 27 to 28:\n\n[code example]\n\nThe server responds with `ok`."
    },
    {
      "id": "delete-a-person",
      "title": "Delete a Person",
      "role": "content",
      "text": "If we know a person's ID, we can delete them from Redis without first having to load their data into a Person object. In the function `delete_person` in `app.py`, we call the `delete` class method on the Person class to do this:\n\n[code example]\n\nLet's delete Dan Harris, the person with ID `01FX8RMR8545RWW4DYCE5MSZA1`:\n\n[code example]\n\nThe server responds with an `ok` response regardless of whether the ID provided existed in Redis."
    },
    {
      "id": "setting-an-expiry-time-for-a-person",
      "title": "Setting an Expiry Time for a Person",
      "role": "configuration",
      "text": "This is an example of how to run arbitrary Redis commands against instances of a model saved in Redis.  Let's see how we can set the time to live (TTL) on a person, so that Redis will expire the JSON document after a configurable number of seconds have passed.\n\nThe function `expire_by_id` in `app.py` handles this as follows.  It takes two parameters: `id` - the ID of a person to expire, and `seconds` - the number of seconds in the future to expire the person after.  This requires us to run the Redis [`EXPIRE`](https://redis.io/docs/latest/commands/expire) command against the person's key.  To do this, we need to access the Redis connection from the `Person` model like so:\n\n[code example]\n\nLet's set the person with ID `01FX8RMR82D091TC37B45RCWY3` to expire in 600 seconds:\n\n[code example]\n\nUsing `redis-cli`, you can check that the person now has a TTL set with the Redis `expire` command:\n\n[code example]\n\nThis shows that Redis will expire the key 584 seconds from now.\n\nYou can use the `.db()` function on your model class to get at the underlying redis-py connection whenever you want to run lower level Redis commands.  For more details, see the [redis-py documentation](https://redis.readthedocs.io/en/stable/)."
    },
    {
      "id": "shutting-down-redis-docker",
      "title": "Shutting Down Redis (Docker)",
      "role": "content",
      "text": "If you're using Docker, and want to shut down the Redis container when you are finished with the application, use `docker-compose down`:\n\n[code example]"
    }
  ],
  "examples": [
    {
      "id": "overview-ex0",
      "language": "json",
      "code": "{\n  \"first_name\": \"A string, the person's first or given name\",\n  \"last_name\": \"A string, the person's last or surname\",\n  \"age\": 36,\n  \"address\": {\n    \"street_number\": 56,\n    \"unit\": \"A string, optional unit number e.g. B or 1\",\n    \"street_name\": \"A string, name of the street they live on\",\n    \"city\": \"A string, name of the city they live in\",\n    \"state\": \"A string, state, province or county that they live in\",\n    \"postal_code\": \"A string, their zip or postal code\",\n    \"country\": \"A string, country that they live in.\"\n  },\n  \"personal_statement\": \"A string, free text personal statement\",\n  \"skills\": [\n    \"A string: a skill the person has\",\n    \"A string: another still that the person has\"\n  ]\n}",
      "section_id": "overview"
    },
    {
      "id": "get-the-source-code-ex0",
      "language": "bash",
      "code": "$ git clone https://github.com/redis-developer/redis-om-python-flask-skeleton-app.git\n$ cd redis-om-python-flask-skeleton-app",
      "section_id": "get-the-source-code"
    },
    {
      "id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex0",
      "language": "bash",
      "code": "$ docker-compose up -d\nCreating network \"redis-om-python-flask-skeleton-app_default\" with the default driver\nCreating redis_om_python_flask_starter ... done",
      "section_id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"
    },
    {
      "id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex1",
      "language": "bash",
      "code": "$ export REDIS_OM_URL=redis://default:<password>@<host>:<port>",
      "section_id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"
    },
    {
      "id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials-ex2",
      "language": "bash",
      "code": "$ export REDIS_OM_URL=redis://default:5uper53cret@enterprise.redis.com:9139",
      "section_id": "start-a-redis-stack-database-or-configure-your-redis-cloud-credentials"
    },
    {
      "id": "create-a-python-virtual-environment-and-install-the-dependencies-ex0",
      "language": "bash",
      "code": "$ python3 -m venv venv\n$ . ./venv/bin/activate\n$ pip install -r requirements.txt",
      "section_id": "create-a-python-virtual-environment-and-install-the-dependencies"
    },
    {
      "id": "start-the-flask-application-ex0",
      "language": "bash",
      "code": "$ export FLASK_ENV=development\n$ flask run",
      "section_id": "start-the-flask-application"
    },
    {
      "id": "start-the-flask-application-ex1",
      "language": "bash",
      "code": "$ flask run\n * Environment: development\n * Debug mode: on\n * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)\n * Restarting with stat\n * Debugger is active!\n * Debugger PIN: XXX-XXX-XXX",
      "section_id": "start-the-flask-application"
    },
    {
      "id": "load-the-sample-data-ex0",
      "language": "bash",
      "code": "$ python dataloader.py\nCreated person Robert McDonald with ID 01FX8RMR7NRS45PBT3XP9KNAZH\nCreated person Kareem Khan with ID 01FX8RMR7T60ANQTS4P9NKPKX8\nCreated person Fernando Ortega with ID 01FX8RMR7YB283BPZ88HAG066P\nCreated person Noor Vasan with ID 01FX8RMR82D091TC37B45RCWY3\nCreated person Dan Harris with ID 01FX8RMR8545RWW4DYCE5MSZA1",
      "section_id": "load-the-sample-data"
    },
    {
      "id": "problems-ex0",
      "language": "py",
      "code": "raise ConnectionError(self._error_message(e))\nredis.exceptions.ConnectionError: Error 61 connecting to localhost:6379. Connection refused.",
      "section_id": "problems"
    },
    {
      "id": "problems-ex1",
      "language": "py",
      "code": "raise ConnectionError(self._error_message(e))\nredis.exceptions.ConnectionError: Error 8 connecting to enterprise.redis.com:9139. nodename nor servname provided, or not known.",
      "section_id": "problems"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex0",
      "language": "py",
      "code": "age: PositiveInt = Field(index=True)",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex1",
      "language": "py",
      "code": "skills: List[str] = Field(index=True)",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex2",
      "language": "py",
      "code": "personal_statement: str = Field(index=True, full_text_search=True)",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex3",
      "language": "py",
      "code": "class Address(EmbeddedJsonModel):\n    # field definitions...",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex4",
      "language": "py",
      "code": "unit: Optional[str] = Field(index=False)",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex5",
      "language": "py",
      "code": "country: str = Field(index=True, default=\"United Kingdom\")",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "building-a-person-model-with-redis-om-ex6",
      "language": "py",
      "code": "address: Address",
      "section_id": "building-a-person-model-with-redis-om"
    },
    {
      "id": "adding-new-people-ex0",
      "language": "py",
      "code": "new_person = Person(**request.json)\n  new_person.save()\n  return new_person.pk",
      "section_id": "adding-new-people"
    },
    {
      "id": "adding-new-people-ex1",
      "language": "bash",
      "code": "curl --location --request POST 'http://127.0.0.1:5000/person/new' \\\n--header 'Content-Type: application/json' \\\n--data-raw '{\n    \"first_name\": \"Joanne\",\n    \"last_name\": \"Peel\",\n    \"age\": 36,\n    \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n    \"address\": {\n      \"street_number\": 56,\n      \"unit\": \"4A\",\n      \"street_name\": \"The Rushes\",\n      \"city\": \"Birmingham\",\n      \"state\": \"West Midlands\",\n      \"postal_code\": \"B91 6HG\",\n      \"country\": \"United Kingdom\"\n    },\n    \"skills\": [\n      \"synths\",\n      \"vocals\",\n      \"guitar\"\n    ]\n}'",
      "section_id": "adding-new-people"
    },
    {
      "id": "examining-the-data-in-redis-ex0",
      "language": "bash",
      "code": "$ redis-cli\n127.0.0.1:6379> json.get :person.Person:01FX8SSSDN7PT9T3N0JZZA758G",
      "section_id": "examining-the-data-in-redis"
    },
    {
      "id": "examining-the-data-in-redis-ex1",
      "language": "bash",
      "code": "$ redis-cli\n127.0.0.1:6379> json.get :person.Person:01FX8SSSDN7PT9T3N0JZZA758G $.address $.skills[0]\n\"{\\\"$.skills[0]\\\":[\\\"synths\\\"],\\\"$.address\\\":[{\\\"pk\\\":\\\"01FX8SSSDNRDSRB3HMVH00NQTT\\\",\\\"street_number\\\":56,\\\"unit\\\":\\\"4A\\\",\\\"street_name\\\":\\\"The Rushes\\\",\\\"city\\\":\\\"Birmingham\\\",\\\"state\\\":\\\"West Midlands\\\",\\\"postal_code\\\":\\\"B91 6HG\\\",\\\"country\\\":\\\"United Kingdom\\\"}]}\"",
      "section_id": "examining-the-data-in-redis"
    },
    {
      "id": "find-a-person-by-id-ex0",
      "language": "py",
      "code": "try:\n      person = Person.get(id)\n      return person.dict()\n  except NotFoundError:\n      return {}",
      "section_id": "find-a-person-by-id"
    },
    {
      "id": "find-a-person-by-id-ex1",
      "language": "bash",
      "code": "curl --location --request GET 'http://localhost:5000/person/byid/01FX8SSSDN7PT9T3N0JZZA758G'",
      "section_id": "find-a-person-by-id"
    },
    {
      "id": "find-a-person-by-id-ex2",
      "language": "json",
      "code": "{\n  \"address\": {\n    \"city\": \"Birmingham\",\n    \"country\": \"United Kingdom\",\n    \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n    \"postal_code\": \"B91 6HG\",\n    \"state\": \"West Midlands\",\n    \"street_name\": \"The Rushes\",\n    \"street_number\": 56,\n    \"unit\": null\n  },\n  \"age\": 36,\n  \"first_name\": \"Joanne\",\n  \"last_name\": \"Peel\",\n  \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n  \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n  \"skills\": [\n    \"synths\",\n    \"vocals\",\n    \"guitar\"\n  ]\n}",
      "section_id": "find-a-person-by-id"
    },
    {
      "id": "find-people-with-matching-first-and-last-name-ex0",
      "language": "py",
      "code": "people = Person.find(\n      (Person.first_name == first_name) &\n      (Person.last_name == last_name)\n  ).all()",
      "section_id": "find-people-with-matching-first-and-last-name"
    },
    {
      "id": "find-people-with-matching-first-and-last-name-ex1",
      "language": "bash",
      "code": "curl --location --request GET 'http://127.0.0.1:5000/people/byname/Kareem/Khan'",
      "section_id": "find-people-with-matching-first-and-last-name"
    },
    {
      "id": "find-people-with-matching-first-and-last-name-ex2",
      "language": "json",
      "code": "{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7THMGA84RH8ZRQRRP9\", \n        \"postal_code\": \"S1 5RE\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"The Beltway\",\n        \"street_number\": 1,\n        \"unit\": \"A\"\n      },\n      \"age\": 27,\n      \"first_name\": \"Kareem\",\n      \"last_name\": \"Khan\",\n      \"personal_statement\":\"I'm Kareem, a multi-instrumentalist and singer looking to join a new rock band.\",\n      \"pk\":\"01FX8RMR7T60ANQTS4P9NKPKX8\",\n      \"skills\": [\n        \"drums\",\n        \"guitar\",\n        \"synths\"\n      ]\n    }\n  ]\n}",
      "section_id": "find-people-with-matching-first-and-last-name"
    },
    {
      "id": "find-people-within-a-given-age-range-ex0",
      "language": "py",
      "code": "people = Person.find(\n      (Person.age >= min_age) &\n      (Person.age <= max_age)\n  ).sort_by(\"age\").all()",
      "section_id": "find-people-within-a-given-age-range"
    },
    {
      "id": "find-people-within-a-given-age-range-ex1",
      "language": "bash",
      "code": "curl --location --request GET 'http://127.0.0.1:5000/people/byage/30/47'",
      "section_id": "find-people-within-a-given-age-range"
    },
    {
      "id": "find-people-within-a-given-age-range-ex2",
      "language": "json",
      "code": "{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7NW221STN6NVRDPEDT\",\n        \"postal_code\": \"S12 2MX\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"Main Street\",\n        \"street_number\": 9,\n        \"unit\": null\n      },\n      \"age\": 35,\n      \"first_name\": \"Robert\",\n      \"last_name\": \"McDonald\",\n      \"personal_statement\": \"My name is Robert, I love meeting new people and enjoy music, coding and walking my dog.\",\n      \"pk\": \"01FX8RMR7NRS45PBT3XP9KNAZH\",\n      \"skills\": [\n        \"guitar\",\n        \"piano\",\n        \"trombone\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Birmingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n        \"postal_code\": \"B91 6HG\",\n        \"state\": \"West Midlands\",\n        \"street_name\": \"The Rushes\",\n        \"street_number\": 56,\n        \"unit\": null\n      },\n      \"age\": 36,\n      \"first_name\": \"Joanne\",\n      \"last_name\": \"Peel\",\n      \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n      \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n      \"skills\": [\n        \"synths\",\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Nottingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR82DDJ90CW8D1GM68YZ\",\n        \"postal_code\": \"NG1 1AA\",\n        \"state\": \"Nottinghamshire\",\n        \"street_name\": \"Broadway\",\n        \"street_number\": 12,\n        \"unit\": \"A-1\"\n      },\n      \"age\": 37,\n      \"first_name\": \"Noor\",\n      \"last_name\": \"Vasan\",\n      \"personal_statement\": \"I sing and play the guitar, I enjoy touring and meeting new people on the road.\",\n      \"pk\": \"01FX8RMR82D091TC37B45RCWY3\",\n      \"skills\": [\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"San Diego\",\n        \"country\": \"United States\",\n        \"pk\": \"01FX8RMR7YCDAVSWBMWCH2B07G\",\n        \"postal_code\": \"92102\",\n        \"state\": \"California\",\n        \"street_name\": \"C Street\",\n        \"street_number\": 1299,\n        \"unit\": null\n      },\n      \"age\": 43,\n      \"first_name\": \"Fernando\",\n      \"last_name\": \"Ortega\",\n      \"personal_statement\": \"I'm in a really cool band that plays a lot of cover songs.  I'm the drummer!\",\n      \"pk\": \"01FX8RMR7YB283BPZ88HAG066P\",\n      \"skills\": [\n        \"clarinet\",\n        \"oboe\",\n        \"drums\"\n      ]\n    }\n  ]\n}",
      "section_id": "find-people-within-a-given-age-range"
    },
    {
      "id": "find-people-in-a-given-city-with-a-specific-skill-ex0",
      "language": "py",
      "code": "people = Person.find(\n      (Person.skills << desired_skill) &\n      (Person.address.city == city)\n  ).all()",
      "section_id": "find-people-in-a-given-city-with-a-specific-skill"
    },
    {
      "id": "find-people-in-a-given-city-with-a-specific-skill-ex1",
      "language": "bash",
      "code": "curl --location --request GET 'http://127.0.0.1:5000/people/byskill/guitar/Sheffield'",
      "section_id": "find-people-in-a-given-city-with-a-specific-skill"
    },
    {
      "id": "find-people-in-a-given-city-with-a-specific-skill-ex2",
      "language": "json",
      "code": "{\n  \"results\": [\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7THMGA84RH8ZRQRRP9\",\n        \"postal_code\": \"S1 5RE\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"The Beltway\",\n        \"street_number\": 1,\n        \"unit\": \"A\"\n      },\n      \"age\": 28,\n      \"first_name\": \"Kareem\",\n      \"last_name\": \"Khan\",\n      \"personal_statement\": \"I'm Kareem, a multi-instrumentalist and singer looking to join a new rock band.\",\n      \"pk\": \"01FX8RMR7T60ANQTS4P9NKPKX8\",\n      \"skills\": [\n        \"drums\",\n        \"guitar\",\n        \"synths\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Sheffield\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR7NW221STN6NVRDPEDT\",\n        \"postal_code\": \"S12 2MX\",\n        \"state\": \"South Yorkshire\",\n        \"street_name\": \"Main Street\",\n        \"street_number\": 9,\n        \"unit\": null\n      },\n      \"age\": 35,\n      \"first_name\": \"Robert\",\n      \"last_name\": \"McDonald\",\n      \"personal_statement\": \"My name is Robert, I love meeting new people and enjoy music, coding and walking my dog.\",\n      \"pk\": \"01FX8RMR7NRS45PBT3XP9KNAZH\",\n      \"skills\": [\n        \"guitar\",\n        \"piano\",\n        \"trombone\"\n      ]\n    }\n  ]\n}",
      "section_id": "find-people-in-a-given-city-with-a-specific-skill"
    },
    {
      "id": "find-people-using-full-text-search-on-their-personal-statements-ex0",
      "language": "py",
      "code": "Person.find(Person.personal_statement % search_term).all()",
      "section_id": "find-people-using-full-text-search-on-their-personal-statements"
    },
    {
      "id": "find-people-using-full-text-search-on-their-personal-statements-ex1",
      "language": "bash",
      "code": "curl --location --request GET 'http://127.0.0.1:5000/people/bystatement/play'",
      "section_id": "find-people-using-full-text-search-on-their-personal-statements"
    },
    {
      "id": "find-people-using-full-text-search-on-their-personal-statements-ex2",
      "language": "json",
      "code": "{\n  \"results\": [\n    { \n      \"address\": {\n        \"city\": \"San Diego\",\n        \"country\": \"United States\",\n        \"pk\": \"01FX8RMR7YCDAVSWBMWCH2B07G\",\n        \"postal_code\": \"92102\",\n        \"state\": \"California\",\n        \"street_name\": \"C Street\",\n        \"street_number\": 1299,\n        \"unit\": null\n      },\n      \"age\": 43,\n      \"first_name\": \"Fernando\",\n      \"last_name\": \"Ortega\",\n      \"personal_statement\": \"I'm in a really cool band that plays a lot of cover songs.  I'm the drummer!\",\n      \"pk\": \"01FX8RMR7YB283BPZ88HAG066P\",\n      \"skills\": [\n        \"clarinet\",\n        \"oboe\",\n        \"drums\"\n      ]\n    }, {\n      \"address\": {\n        \"city\": \"Nottingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8RMR82DDJ90CW8D1GM68YZ\",\n        \"postal_code\": \"NG1 1AA\",\n        \"state\": \"Nottinghamshire\",\n        \"street_name\": \"Broadway\",\n        \"street_number\": 12,\n        \"unit\": \"A-1\"\n      },\n      \"age\": 37,\n      \"first_name\": \"Noor\",\n      \"last_name\": \"Vasan\",\n      \"personal_statement\": \"I sing and play the guitar, I enjoy touring and meeting new people on the road.\",\n      \"pk\": \"01FX8RMR82D091TC37B45RCWY3\",\n      \"skills\": [\n        \"vocals\",\n        \"guitar\"\n      ]\n    },\n    {\n      \"address\": {\n        \"city\": \"Birmingham\",\n        \"country\": \"United Kingdom\",\n        \"pk\": \"01FX8SSSDNRDSRB3HMVH00NQTT\",\n        \"postal_code\": \"B91 6HG\",\n        \"state\": \"West Midlands\",\n        \"street_name\": \"The Rushes\",\n        \"street_number\": 56,\n        \"unit\": null\n      },\n      \"age\": 36,\n      \"first_name\": \"Joanne\",\n      \"last_name\": \"Peel\",\n      \"personal_statement\": \"Music is my life, I love gigging and playing with my band.\",\n      \"pk\": \"01FX8SSSDN7PT9T3N0JZZA758G\",\n      \"skills\": [\n        \"synths\",\n        \"vocals\",\n        \"guitar\"\n      ]\n    }\n  ]\n}",
      "section_id": "find-people-using-full-text-search-on-their-personal-statements"
    },
    {
      "id": "update-a-persons-age-ex0",
      "language": "py",
      "code": "try:\n      person = Person.get(id)\n\n  except NotFoundError:\n      return \"Bad request\", 400",
      "section_id": "update-a-persons-age"
    },
    {
      "id": "update-a-persons-age-ex1",
      "language": "py",
      "code": "person.age = new_age\n  person.save()",
      "section_id": "update-a-persons-age"
    },
    {
      "id": "update-a-persons-age-ex2",
      "language": "bash",
      "code": "curl --location --request POST 'http://127.0.0.1:5000/person/01FX8RMR7T60ANQTS4P9NKPKX8/age/28'",
      "section_id": "update-a-persons-age"
    },
    {
      "id": "delete-a-person-ex0",
      "language": "py",
      "code": "Person.delete(id)",
      "section_id": "delete-a-person"
    },
    {
      "id": "delete-a-person-ex1",
      "language": "bash",
      "code": "curl --location --request POST 'http://127.0.0.1:5000/person/01FX8RMR8545RWW4DYCE5MSZA1/delete'",
      "section_id": "delete-a-person"
    },
    {
      "id": "setting-an-expiry-time-for-a-person-ex0",
      "language": "py",
      "code": "person_to_expire = Person.get(id)\n  Person.db().expire(person_to_expire.key(), seconds)",
      "section_id": "setting-an-expiry-time-for-a-person"
    },
    {
      "id": "setting-an-expiry-time-for-a-person-ex1",
      "language": "bash",
      "code": "curl --location --request POST 'http://localhost:5000/person/01FX8RMR82D091TC37B45RCWY3/expire/600'",
      "section_id": "setting-an-expiry-time-for-a-person"
    },
    {
      "id": "setting-an-expiry-time-for-a-person-ex2",
      "language": "bash",
      "code": "127.0.0.1:6379> ttl :person.Person:01FX8RMR82D091TC37B45RCWY3\n(integer) 584",
      "section_id": "setting-an-expiry-time-for-a-person"
    },
    {
      "id": "shutting-down-redis-docker-ex0",
      "language": "bash",
      "code": "$ docker-compose down\nStopping redis_om_python_flask_starter ... done\nRemoving redis_om_python_flask_starter ... done\nRemoving network redis-om-python-flask-skeleton-app_default",
      "section_id": "shutting-down-redis-docker"
    }
  ]
}
