Are routes in flask matched from top to bottom năm 2024

It's hard to imagine a more critical feature of web frameworks than routing: the humble act of mapping URLs to actions, such as serving pages or data. It isn't often you find somebody sad or miserable enough to expand on such an inglorious feature. As it turns out, I am miserable enough to be the kind of person who writes tutorials about routing.

At first glance, it's hard to imagine routing as an "art." We'll typically reserve a URL path, such as

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
5 or

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
6, associate this with a page template, and serve said template to the user, perhaps with added business logic. That perspective works fine for small-scale applications, but meaningful applications (or APIs) aren't static one-to-one mappings. Apps are a medium for data such as user-generated content such as user profiles or author posts, and routes define how our users will access data, which is always changing. To build products larger than ourselves, we need to arm them with the ability to grow in ways we can't foresee, which means defining dynamic routing opportunities that can grow endlessly. This is where we have the chance to be artistic.

Today, we're covering defining and building smart routes to accommodate dynamic applications and APIs. If you have any prior experience with MVC frameworks, you should be able to follow along just fine; almost no previous knowledge of Flask is needed to keep up.

Defining Routes & Views

Every web framework begins with the concept of serving content at a given URL. Routes refer to URL patterns of an app (such as myapp.com/home or myapp.com/about). Views refer to the content served at these URLs, whether said content is a JSON response, a rendered template, or otherwise.

Flask's "Hello world" example defines a route listening at the root of our app and defines a route function called

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
7:
from flask import Flask
app = Flask(__name__)
@app.route("/")
def home():
    return "Hello World!"

Flask's "Hello world!"

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
8 is a Python decorator that Flask provides to assign URLs in our app to functions easily. It's easy to understand what is happening at first glance: the decorator is telling our

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
9 that whenever a user visits our app domain (myapp.com) at the given
from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

0, execute the

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
7 function. If you aren't familiar with Python decorators, they're essentially logic that "wraps" other functions; they always match the syntax of being a line above the function they're modifying.

The names we chose for our view functions hold significance in Flask. Years of web development taught us that URLs change all the time, typically for business or SEO purposes, whereas the names of pages generally names stay the same. Flask recognizes this by giving us ways to move users around our app by referring to the names of views by essentially saying, "redirect the user to whatever the URL for

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
7 happens to be." We'll demonstrate that in a moment, but it's worth recognizing the importance of view names.

We can handle multiple routes with a single function by simply stacking additional route decorators above any route! The following is a valid example of serving the same "Hello World!" message for 3 separate routes:

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"

Route HTTP Methods

In addition to accepting the URL of a route as a parameter, the

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
8 decorator can accept a second argument: a list of accepted HTTP methods. Flask views will only accept GET requests by default unless the route decorator explicitly lists which HTTP methods the view should honor. Providing a list of accepted methods is a good way to build constraints into the route for a REST API endpoint, which only makes sense in specific contexts.
from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

Route defined with explicitly defined accepted HTTP methods

Dynamic Routes & Variable Rules

Static route URLs can only get us so far, as modern-day web applications are rarely straightforward. Let's say we want to create a profile page for every user that creates an account within our app or dynamically generate article URLs based on the publication date. Here is where variable rules come in.

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

Dynamic values in route names

When defining our route, values within carrot brackets

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

4 indicate a variable; this enables routes to be dynamically generated. Variables can be type-checked by adding a colon, followed by the data type constraint. Routes can accept the following variable types:

  • string: Accepts any text without a slash (the default).
  • int: An integer (such as an ID or any value to be parsed as an integer).
  • float: Accepts numerical values containing decimal points.
  • path: Similar to a string, but accepts slashes.

Unlike static routes, routes created with variable rules do accept parameters, with those parameters being the route variables themselves.

Types of View Responses

Now that we're industry-leading experts in defining route URLs, we'll focus on something more involved: route logic. First, we should recap the types of responses a view can result in. The top 3 common ways a route will conclude will be by generating a page template, providing a response, or redirecting the user somewhere else (we briefly looked over these in part 1).

Remember: views always conclude with a

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

5 statement. Whenever we encounter a

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

5 statement in a route, we tell the function to serve whatever we return to the user.

Rendering Page Templates

You're probably aware by now that Flask serves webpages via a built-in templating engine called Jinja2 (and if you don't know, now you know). To render a Jinja2 page template, we first must import a built-in Flask function called

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7. When a view function returns

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7. This tells Flask to render a Jinja2 template to serve as an HTML page to the user. Of course, this means we need to be sure our app has a templates directory:

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

Setting a "templates" directory

With the above, our app knows that calling

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7 in a Flask route will look in our app's /templates folder for the template we pass in. In full, such a route looks like this:

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

Render a page template

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7 accepts one positional argument, which is the name of the template found in our templates folder (in this case, index.jinja2). In addition, we can pass values to our template as keyword arguments. For example, if we want to set the title and content of our template via our view as opposed to hardcoding them, into our template, we can do so:

"""Flask application with a route that returns a dynamically rendered page.""" 
from flask import Flask, render_template
# Initialize Flask app
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template(
        'index.jinja2',
        title='Flask-Login Tutorial.',
        body="You are now logged in!"
    )

Render template with passed variables

For a more in-depth look into how rendering templates work in Flask, check out our piece about creating Jinja templates.

Making a Response Object

If we're building an endpoint intended to respond with information to be used programmatically, serving page templates isn't what we need. Instead, we should look to

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1.

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1 allows us to serve up information while providing a status code (such as 200 or 500) and attaching headers to the said response. We can even use

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1 in tandem with

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7 if we want to serve up templates with specific headers! Most of the time

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1 is used to provide information in the form of JSON objects:

"""Flask application with a route that returns JSON response.""" 
from flask import Flask, make_response
# Initialize Flask app
app = Flask(__name__)
@app.route("/api/v2/test_response")
def users():
    headers = {"Content-Type": "application/json"}
    return make_response(
        'Test worked!',
        200,
        headers=headers
    )

Flask route that returns a JSON response

There are three arguments we can pass to

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1. The first is the body of our response, usually a JSON object or message. Next is a 3-digit integer representing the HTTP response code we provide to the requester. Finally, we can pass response headers if you so choose.

Redirecting Users Between Views

The last of our big three route resolutions is

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

7. Redirect accepts a string, which will be the path to redirect the user to. This can be a relative path, an absolute path, or even an external URL:

"""Flask application with a route that redirects users to another URL.""" 
from flask import Flask, redirect
# Initialize Flask app
app = Flask(__name__)
@app.route("/login")
def login():
    return redirect('/dashboard.html')

Route redirects

But wait! Remember when we said it's best practice to refer to routes by their names and not by their URL patterns? That's where we use

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

8 comes in: this built-in function takes the name of a view function as input, and will output the URL route of the provided view. This means changing route URLs won't result in broken links between pages! Below is the correct way to achieve what we did above:

"""Flask application with a route that redirects users to another URL.""" 
from flask import Flask, redirect, url_for
# Initialize Flask app
app = Flask(__name__)
@app.route("/login")
def login():
    return redirect(url_for('dashboard'))

Redirecting to route names

Building Smarter Views

Building a respectable view requires us to excel at both the soft skills of working with web frameworks and the hard skills of merely knowing the tools available to us.

The basic "soft skill" of building a route is conceptually straightforward but difficult in practice for many newcomers. I'm referring to the basics of MVC: the concept that views should only contain logic that resolves the response of a view (not extraneous business logic that should be encapsulated elsewhere). It's a skill that comes from habit and example: a bit out of this post's scope but bears repeating regardless.

Luckily, the "hard skills" are a bit more straightforward. Here are a couple of tools essential to building elegant routes.

The Request Object

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

9 is one of the "global" objects we mentioned earlier. It's available to every route and contains all the context of a request made to the said route. Take a look at what things are attached to

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

0 which we can access in a route:

  • request.method: Contains the method used to access a route, such as GET or POST. from flask import Flask, render_template app = Flask(name, template_folder="templates")

    1 is essential for building smart routes: we can use this logic to have one route serve multiple different responses depending on the method used to call said route. This is how REST APIs provide different results on a GET request versus a POST request (

    from flask import Flask, render_template app = Flask(name, template_folder="templates") 2 can open a block only pertaining to POST requests in our route).
  • request.args: Contains the query-string parameters of a request that hit our route. If we're building an endpoint that accepts a url parameter, for example, we can get this from the request as from flask import Flask, render_template app = Flask(name, template_folder="templates") 3.
  • request.data: Returns the body of an object posted to a route.
  • request.form: If a user hits this route as a result of form submission, from flask import Flask, render_template app = Flask(name, template_folder="templates")

    4 it is our way of accessing the information the form posted. For example, to fetch the provided username of a submitted form

    from flask import Flask, render_template app = Flask(name, template_folder="templates") 5 is used.
  • request.headers: Contains the HTTP response headers of a request.

Here’s an example I took from a popular plugin called

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

6: a library that handles user authentication in Flask. This is a complex example of a view that utilizes most things we just covered in a single route. I don’t expect you to understand everything that’s happening here, but it’s good to see how powerful and versatile we can make a single route simply by using the properties of

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

0:

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
0

Example of a complex Flask route

The "g" Object

Let's say we want a view to access data that isn't passed along as part of a

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

0 object. We already know we can't pass parameters to routes traditionally: this is where we can use Flask's

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

9. "G" stands for "global," which isn't a great name since we're restricted by the application context, but that's neither here nor there. The gist is that

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

9 is an object we can attach values to.

We assign values to

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

9 as such:

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
1

Assign a value to

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

2

Once set, accessing

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

3 will give us

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

4, even inside a route.

The preferred way of purging values from

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

9 is by using

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

6:

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
2

Removing a value from

"""Flask application with a route that returns a static page.""" 
from flask import Flask, render_template
# Initialize Flask app
# Note the configuration to serve Jinja2 from the `/templates` directory
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template("index.jinja2")

2

It's best not to dwell on

from flask import Flask, render_template
app = Flask(__name__, template_folder="templates")

9 for too long. It is useful in some situations but can quickly become confusing and unnecessary: most of what we need can be handled by the

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

9 object.

We've seen how to map static and dynamic routes to functions/views using the

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
8 decorator. Flask also empowers us with several powerful decorators to supplement the routes we create with
from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

0:

  • """Flask application with a route that returns a dynamically rendered page.""" from flask import Flask, render_template

    Initialize Flask app

    app = Flask(
    name,  
    template_folder="templates"  
    
    ) @app.route("/") def home():
    """Serve homepage template."""  
    return render_template(  
        'index.jinja2',  
        title='Flask-Login Tutorial.',  
        body="You are now logged in!"  
    )  
    

    2: Defining a function with the

    """Flask application with a route that returns a dynamically rendered page.""" from flask import Flask, render_template

    Initialize Flask app

    app = Flask(
    name,  
    template_folder="templates"  
    
    ) @app.route("/") def home():
    """Serve homepage template."""  
    return render_template(  
        'index.jinja2',  
        title='Flask-Login Tutorial.',  
        body="You are now logged in!"  
    )  
    
    3 decorator will execute said function before every request is made. Examples of when we might use this could include tracking user actions, determining user permissions, or adding a "back button" feature by remembering the last page the user visited before loading the next.
  • """Flask application with a route that returns a dynamically rendered page.""" from flask import Flask, render_template

    Initialize Flask app

    app = Flask(
    name,  
    template_folder="templates"  
    
    ) @app.route("/") def home():
    """Serve homepage template."""  
    return render_template(  
        'index.jinja2',  
        title='Flask-Login Tutorial.',  
        body="You are now logged in!"  
    )  
    

    4: Setting an "endpoint" is an alternative to ... @app.route("/") @app.route("/home") @app.route("/index") def home():

    return "Hello World!"  
    
    8 that accomplishes the same effect of mapping URLs to logic. This is a fairly advanced use case

    """Flask application with a route that returns a dynamically rendered page.""" from flask import Flask, render_template

    Initialize Flask app

    app = Flask(
    name,  
    template_folder="templates"  
    
    ) @app.route("/") def home():
    """Serve homepage template."""  
    return render_template(  
        'index.jinja2',  
        title='Flask-Login Tutorial.',  
        body="You are now logged in!"  
    )  
    
    6 that comes into play in larger applications when certain logic must transverse modules known as Blueprints (don't worry if this terminology sounds like nonsense — most people will likely never run into a use case for endpoints).

Error handling

What happens when a user of our app experiences a fatal error? Flask provides us with

"""Flask application with a route that returns a dynamically rendered page.""" 
from flask import Flask, render_template
# Initialize Flask app
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template(
        'index.jinja2',
        title='Flask-Login Tutorial.',
        body="You are now logged in!"
    )

7a decorator called

"""Flask application with a route that returns a dynamically rendered page.""" 
from flask import Flask, render_template
# Initialize Flask app
app = Flask(
    __name__,
    template_folder="templates"
)
@app.route("/")
def home():
    """Serve homepage template."""
    return render_template(
        'index.jinja2',
        title='Flask-Login Tutorial.',
        body="You are now logged in!"
    )

8, which serves as a catch-all route for a given HTTP error. Whenever a user experiences the error code we pass to this decorator, Flask immediately serves a corresponding view:

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
3

Route for handling 404 errors returned by the server

See how we used

...
@app.route('/user/<username>')
def profile(username):
    # Logic goes here
@app.route('/<int:year>/<int:month>/<title>')
def article(year, month, title):
    # Logic goes here

1 in conjunction with

from flask import Flask
app = Flask(__name__)
@app.route("/api/v1/users/", methods=['GET', 'POST', 'PUT'])
def users():
    # Logic goes here

7? Not only did we serve up a custom template, but we also provided the correct error message to the browser. We're coming full circle!

The above example passes 404 to the

"""Flask application with a route that returns JSON response.""" 
from flask import Flask, make_response
# Initialize Flask app
app = Flask(__name__)
@app.route("/api/v2/test_response")
def users():
    headers = {"Content-Type": "application/json"}
    return make_response(
        'Test worked!',
        200,
        headers=headers
    )

1 decorator, but we can pass any numerical HTTP error code. We might not know the nuances of how a complex app will fail under a load of thousands of users, but unforeseen errors will certainly occur. We can (and should) account for errors before they occur by using the

"""Flask application with a route that returns JSON response.""" 
from flask import Flask, make_response
# Initialize Flask app
app = Flask(__name__)
@app.route("/api/v2/test_response")
def users():
    headers = {"Content-Type": "application/json"}
    return make_response(
        'Test worked!',
        200,
        headers=headers
    )

1 decorator to ensure our users won't be left in the dark.

... @app.route("/") @app.route("/home") @app.route("/index") def home():

return "Hello World!"
4

Routes for various HTTP error codes

Additional Route Decorators via Flask Plugins

Lastly, I can't let you go without at least mentioning some of the awesome decorators provided by Flask's powerful plugins. These are a testament to how powerful Flask routes can become with the addition of custom decorators:

  • """Flask application with a route that returns JSON response.""" from flask import Flask, make_response

    Initialize Flask app

    app = Flask(name) @app.route("/api/v2/test_response") def users():
    headers = {"Content-Type": "application/json"}  
    return make_response(  
        'Test worked!',  
        200,  
        headers=headers  
    )  
    
    3 (from Flask-Login): Slap this before any route to immediately protect it from logged-out users' access. If the user is logged in, @login_required lets them in accordingly. If you're interested in user account management in Flask, check our post about Flask-Login.
  • """Flask application with a route that returns JSON response.""" from flask import Flask, make_response

    Initialize Flask app

    app = Flask(name) @app.route("/api/v2/test_response") def users():
    headers = {"Content-Type": "application/json"}  
    return make_response(  
        'Test worked!',  
        200,  
        headers=headers  
    )  
    
    4 (from Flask-Admin): Allows views to be created for a custom admin panel.
  • """Flask application with a route that returns JSON response.""" from flask import Flask, make_response

    Initialize Flask app

    app = Flask(name) @app.route("/api/v2/test_response") def users():
    headers = {"Content-Type": "application/json"}  
    return make_response(  
        'Test worked!',  
        200,  
        headers=headers  
    )  
    

    5 (from Flask-Cache): Cache routes for a set period, ie:

    """Flask application with a route that returns JSON response.""" from flask import Flask, make_response

    Initialize Flask app

    app = Flask(name) @app.route("/api/v2/test_response") def users():
    headers = {"Content-Type": "application/json"}  
    return make_response(  
        'Test worked!',  
        200,  
        headers=headers  
    )  
    
    6.

On The Route to Greatness

Clearly, there's a lot we can do with routes in Flask. Newcomers typically get started with Flask armed only with basic knowledge of routes and responses and manage to get along fine. The beauty of Flask development is the flexibility to build meaningful software immediately with the ability to add functionality when the time is right, as opposed to upfront.

We've probably covered more route-related logic than what 95% of Flask apps currently need or utilize in the wild. You're well-equipped to build some cool stuff already, so stop reading and get coding!

How are routes defined in Flask?

Routes in Flask are defined using the @app. route() decorator, which is applied to a Python function. The decorator takes one mandatory argument: the URL rule or path, and optional arguments such as methods allowed for the route.

How do you specify a path in Flask?

Flask allows you to create a URL route that matches any path by using the special path:variable_name converter in your URL rule. For example, @app. route('/path/path:subpath') would match any path after /path/, and pass it as a string to your view function.

Is app routing in Flask used to map a specific URL?

App routing is the technique used to map the specific URL with the associated function intended to perform some task. The Latest Web frameworks use the routing technique to help users remember application URLs. It is helpful to access the desired page directly without navigating from the home page.

What does app Flask (__ name __) do?

app = Flask(__name__) creates a Flask application object — app — in the current Python module. A Python module is just a Python file, filename.py. An object (in Python and in other programming languages) is a data type that can include a ton of functions, methods, and attributes.