mirror of https://github.com/tiangolo/fastapi.git
📝 Update and add docs for dependencies
This commit is contained in:
parent
332ee4aee1
commit
b79c13baed
|
|
@ -1,5 +1,4 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
|
@ -7,18 +6,15 @@ app = FastAPI()
|
|||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
class CommonQueryParams(BaseModel):
|
||||
q: str = None
|
||||
skip: int = None
|
||||
limit: int = None
|
||||
|
||||
|
||||
async def common_parameters(q: str = None, skip: int = 0, limit: int = 100):
|
||||
return CommonQueryParams(q=q, skip=skip, limit=limit)
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: CommonQueryParams = Depends(common_parameters)):
|
||||
async def read_items(commons: CommonQueryParams = Depends(CommonQueryParams)):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
|
|
|
|||
|
|
@ -1,34 +1,23 @@
|
|||
from typing import List
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InterestsTracker(BaseModel):
|
||||
track_code: str
|
||||
interests: List[str]
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
fake_tracked_users_db = {
|
||||
"Foo": {"track_code": "Foo", "interests": ["sports", "movies"]},
|
||||
"Bar": {"track_code": "Bar", "interests": ["food", "shows"]},
|
||||
"Baz": {"track_code": "Baz", "interests": ["gaming", "virtual reality"]},
|
||||
}
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
async def get_tracked_interests(track_code: str = Cookie(None)):
|
||||
if track_code in fake_tracked_users_db:
|
||||
track_dict = fake_tracked_users_db[track_code]
|
||||
track = InterestsTracker(**track_dict)
|
||||
return track
|
||||
return None
|
||||
|
||||
|
||||
@app.get("/interests/")
|
||||
async def read_interests(
|
||||
tracked_interests: InterestsTracker = Depends(get_tracked_interests)
|
||||
):
|
||||
response = {"interests": tracked_interests.interests}
|
||||
@app.get("/items/")
|
||||
async def read_items(commons=Depends(CommonQueryParams)):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -1,49 +1,23 @@
|
|||
from random import choice
|
||||
from typing import List
|
||||
|
||||
from fastapi import Cookie, Depends, FastAPI
|
||||
from pydantic import BaseModel
|
||||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class InterestsTracker(BaseModel):
|
||||
track_code: str
|
||||
interests: List[str]
|
||||
fake_items_db = [{"item_name": "Foo"}, {"item_name": "Bar"}, {"item_name": "Baz"}]
|
||||
|
||||
|
||||
fake_tracked_users_db = {
|
||||
"Foo": {"track_code": "Foo", "interests": ["sports", "movies"]},
|
||||
"Bar": {"track_code": "Bar", "interests": ["food", "shows"]},
|
||||
"Baz": {"track_code": "Baz", "interests": ["gaming", "virtual reality"]},
|
||||
}
|
||||
class CommonQueryParams:
|
||||
def __init__(self, q: str = None, skip: int = 0, limit: int = 100):
|
||||
self.q = q
|
||||
self.skip = skip
|
||||
self.limit = limit
|
||||
|
||||
|
||||
async def get_tracked_interests(track_code: str = Cookie(None)):
|
||||
if track_code in fake_tracked_users_db:
|
||||
track_dict = fake_tracked_users_db[track_code]
|
||||
track = InterestsTracker(**track_dict)
|
||||
return track
|
||||
return None
|
||||
|
||||
|
||||
class ComplexTracker:
|
||||
def __init__(self, tracker: InterestsTracker = Depends(get_tracked_interests)):
|
||||
self.tracker = tracker
|
||||
|
||||
def random_interest(self):
|
||||
"""
|
||||
Get a random interest from the tracked ones for the current user.
|
||||
If the user doesn't have tracked interests, return a random one from the ones available.
|
||||
"""
|
||||
if self.tracker.interests:
|
||||
return choice(self.tracker.interests)
|
||||
return choice(
|
||||
["sports", "movies", "food", "shows", "gaming", "virtual reality"]
|
||||
)
|
||||
|
||||
|
||||
@app.get("/suggested-category")
|
||||
async def read_suggested_category(tracker: ComplexTracker = Depends(None)):
|
||||
response = {"category": tracker.random_interest()}
|
||||
@app.get("/items/")
|
||||
async def read_items(commons: CommonQueryParams = Depends()):
|
||||
response = {}
|
||||
if commons.q:
|
||||
response.update({"q": commons.q})
|
||||
items = fake_items_db[commons.skip : commons.limit]
|
||||
response.update({"items": items})
|
||||
return response
|
||||
|
|
|
|||
|
|
@ -0,0 +1,20 @@
|
|||
from fastapi import Cookie, Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
def query_extractor(q: str = None):
|
||||
return q
|
||||
|
||||
|
||||
def query_or_cookie_extractor(
|
||||
q: str = Depends(query_extractor), last_query: str = Cookie(None)
|
||||
):
|
||||
if not q:
|
||||
return last_query
|
||||
return q
|
||||
|
||||
|
||||
@app.get("/items/")
|
||||
async def read_query(query_or_default: str = Depends(query_or_cookie_extractor)):
|
||||
return {"q_or_cookie": query_or_default}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
from fastapi import Depends, FastAPI
|
||||
|
||||
app = FastAPI()
|
||||
|
||||
|
||||
class FixedContentQueryChecker:
|
||||
def __init__(self, fixed_content: str):
|
||||
self.fixed_content = fixed_content
|
||||
|
||||
def __call__(self, q: str = ""):
|
||||
if q:
|
||||
return self.fixed_content in q
|
||||
return False
|
||||
|
||||
|
||||
checker = FixedContentQueryChecker("bar")
|
||||
|
||||
|
||||
@app.get("/query-checker/")
|
||||
async def read_query_check(fixed_content_included: bool = Depends(checker)):
|
||||
return {"fixed_content_in_query": fixed_content_included}
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
!!! danger
|
||||
This is, more or less, an "advanced" chapter.
|
||||
|
||||
If you are just starting with **FastAPI** you might want to skip this chapter and come back to it later.
|
||||
|
||||
## Parameterized dependencies
|
||||
|
||||
All the dependencies we have seen are a fixed function or class.
|
||||
|
||||
But there could be cases where you want to be able to set parameters on the dependency, without having to declare many different functions or classes.
|
||||
|
||||
Let's imagine that we want to have a dependency that checks if the query parameter `q` contains some fixed content.
|
||||
|
||||
But we want to be able to parameterize that fixed content.
|
||||
|
||||
## A "callable" instance
|
||||
|
||||
In Python there's a way to make an instance of a class a "callable".
|
||||
|
||||
Not the class itself (which is already a callable), but an instance of that class.
|
||||
|
||||
To do that, we declare a method `__call__`:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
## Parameterize the instance
|
||||
|
||||
And now, we can use `__init__` to declare the parameters of the instance that we can use to "parameterize" the dependency:
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
In this case, **FastAPI** won't ever touch or care about `__init__`, we will use it directly in our code.
|
||||
|
||||
## Create an instance
|
||||
|
||||
We could create an instance of this class with:
|
||||
|
||||
```Python hl_lines="16"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
And that way we are able to "parameterize" our dependency, that now has `"bar"` inside of it, as the attribute `checker.fixed_content`.
|
||||
|
||||
## Use the instance as a dependency
|
||||
|
||||
Then, we could use this `checker` in a `Depends(checker)`, instead of `Depends(FixedContentQueryChecker)`, because the dependency is the instance, `checker`, not the class itself.
|
||||
|
||||
And when solving the dependency, **FastAPI** will call this `checker` like:
|
||||
|
||||
```Python
|
||||
checker(q="somequery")
|
||||
```
|
||||
|
||||
...and pass whatever that returns as the value of the dependency in our path operation function as the parameter `fixed_content_included`:
|
||||
|
||||
```Python hl_lines="20"
|
||||
{!./src/dependencies/tutorial006.py!}
|
||||
```
|
||||
|
||||
!!! tip
|
||||
All this might seem contrived. And it might not be very clear how is it useful yet.
|
||||
|
||||
These examples are intentionally simple, but show how it all works.
|
||||
|
||||
In the chapters about security, you will be using utility functions that are implemented in this same way.
|
||||
|
||||
If you understood all this, you already know how those utility tools for security work underneath.
|
||||
|
|
@ -0,0 +1,177 @@
|
|||
Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example.
|
||||
|
||||
## A `dict` from the previous example
|
||||
|
||||
In the previous example, we where returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the path operation function.
|
||||
|
||||
And we know that `dict`s can't provide a lot of editor support because they can't know their keys and value types.
|
||||
|
||||
We can do better...
|
||||
|
||||
## What makes a dependency
|
||||
|
||||
Up to now you have seen dependencies declared as functions.
|
||||
|
||||
But that's not the only way to declare dependencies (although it would probably be the more common).
|
||||
|
||||
The key factor is that a dependency should be a "callable".
|
||||
|
||||
A "**callable**" in Python is anything that Python can "call" like a function.
|
||||
|
||||
So, if you have an object `something` (that might _not_ be a function) and you can do:
|
||||
|
||||
```Python
|
||||
something()
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```Python
|
||||
something(some_argument, some_keyword_argument="foo")
|
||||
```
|
||||
|
||||
then it is a "callable".
|
||||
|
||||
## Classes as dependencies
|
||||
|
||||
You might notice that to create an instance of a Python class, you use that same syntax.
|
||||
|
||||
So, a Python class is also a **callable**.
|
||||
|
||||
Then, in **FastAPI**, you could use a Python class as a dependency.
|
||||
|
||||
What FastAPI actually checks is that it is a "callable" (function, class or anything else) and the parameters defined.
|
||||
|
||||
If you pass a "callable" as a dependency in **FastAPI**, it will analyze the parameters for that "callable", and process them in the same way as the parameters for a path operation function. Including sub-dependencies.
|
||||
|
||||
That also applies to callables with no parameters at all. The same as would be for path operation functions with no parameteres.
|
||||
|
||||
Then, we can change the dependency "dependable" `common_parameters` from above to the class `CommonQueryParameters`:
|
||||
|
||||
```Python hl_lines="9 10 11 12 13"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
Pay attention to the `__init__` method used to create the instance of the class:
|
||||
|
||||
```Python hl_lines="10"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
...it has the same parameters as our previous `common_parameters`:
|
||||
|
||||
```Python hl_lines="6"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
Those parameters are what **FastAPI** will use to "solve" the dependency.
|
||||
|
||||
In both cases, it will have:
|
||||
|
||||
* an optional `q` query parameter.
|
||||
* a `skip` query parameter, with a default of `0`.
|
||||
* a `limit` query parameter, with a default of `100`.
|
||||
|
||||
In both cases the data will be converted, validated, documented on the OpenAPI schema, etc.
|
||||
|
||||
## Use it
|
||||
|
||||
Now you can declare your dependency using this class.
|
||||
|
||||
And as when **FastAPI** calls that class the value that will be passed as `commons` to your function will be an "instance" of the class, you can declare that parameter `commons` to be of type of the class, `CommonQueryParams`.
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Type annotation vs `Depends`
|
||||
|
||||
In the code above, you are declaring `commons` as:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
The last `CommonQueryParams`, in:
|
||||
|
||||
```Python
|
||||
... = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...is what **FastAPI** will actually use to know what is the dependency.
|
||||
|
||||
From it is that FastAPI will extract the declared parameters and that is what FastAPI will actually call.
|
||||
|
||||
---
|
||||
|
||||
In this case, the first `CommonQueryParams`, in:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams ...
|
||||
```
|
||||
|
||||
...doesn't have any special meaning for **FastAPI**. FastAPI won't use it for data conversion, validation, etc. (as it is using the `= Depends(CommonQueryParams)` for that).
|
||||
|
||||
You could actually write just:
|
||||
|
||||
```Python
|
||||
commons = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
..as in:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial003.py!}
|
||||
```
|
||||
|
||||
|
||||
But declaring the type is encouraged as that way your editor will know what will be passed as the parameter `commons`, and then it can help you with code completion, type checks, etc:
|
||||
|
||||
```Python hl_lines="19 20 21"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Shortcut
|
||||
|
||||
But you see that we are having some code repetition here, writing `CommonQueryParams` twice:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
**FastAPI** provides a shortcut for these cases, in where the dependency is *specifically* a class that **FastAPI** will "call" to create an instance of the class itself.
|
||||
|
||||
For those specific cases, you can do the following:
|
||||
|
||||
Instead of writing:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(CommonQueryParams)
|
||||
```
|
||||
|
||||
...you write:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends()
|
||||
```
|
||||
|
||||
So, you can declare the dependency as the type of the variable, and use `Depends()` as the "default" value, without any parameter, instead of having to write the full class *again* inside of `Depends(CommonQueryParams)`.
|
||||
|
||||
So, the same example would look like:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial004.py!}
|
||||
```
|
||||
|
||||
...and **FastAPI** will know what to do.
|
||||
|
||||
!!! tip
|
||||
If all that seems more confusing than helpful, disregard it, you don't *need* it.
|
||||
|
||||
It is just a shortcut. Because **FastAPI** cares about helping you minimize code repetition.
|
||||
|
|
@ -22,7 +22,7 @@ That's it.
|
|||
|
||||
And it has the same shape and structure that all your path operation functions.
|
||||
|
||||
You can think of it as a path operation function without the "decorator" (the `@app.get("/some-path")`).
|
||||
You can think of it as a path operation function without the "decorator" (without the `@app.get("/some-path")`).
|
||||
|
||||
And it can return anything you want.
|
||||
|
||||
|
|
@ -1,72 +0,0 @@
|
|||
Before diving deeper into the **Dependency Injection** system, let's upgrade the previous example.
|
||||
|
||||
## A `dict` from the previous example
|
||||
|
||||
In the previous example, we where returning a `dict` from our dependency ("dependable"):
|
||||
|
||||
```Python hl_lines="7"
|
||||
{!./src/dependencies/tutorial001.py!}
|
||||
```
|
||||
|
||||
But then we get a `dict` in the parameter `commons` of the path operation function.
|
||||
|
||||
And we know that `dict`s can't provide a lot of editor support because they can't know their keys and value types.
|
||||
|
||||
## Create a Pydantic model
|
||||
|
||||
But we are already using Pydantic models in other places and we have already seen all the benefits.
|
||||
|
||||
Let's use them here too.
|
||||
|
||||
Create a model for the common parameters (and don't pay attention to the rest, for now):
|
||||
|
||||
```Python hl_lines="11 12 13 14"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Return a Pydantic model
|
||||
|
||||
Now we can return a Pydantic model from the dependency ("dependable") with the same data as the dict before:
|
||||
|
||||
```Python hl_lines="17"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
## Declare the Pydantic model
|
||||
|
||||
We can now come back to the path operation function and declare the type of the `commons` parameter to be that Pydantic model:
|
||||
|
||||
```Python
|
||||
commons: CommonQueryParams = Depends(common_parameters)
|
||||
```
|
||||
|
||||
It won't be interpreted as a JSON request `Body` because we are using `Depends`:
|
||||
|
||||
```Python hl_lines="21"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
In the case of dependencies with `Depends`, the type of the parameter is only to get editor support.
|
||||
|
||||
Your dependencies won't be enforced to return a specific type of data.
|
||||
|
||||
## Use the Pydantic model
|
||||
|
||||
And now we can use that model in our code, with all the lovable editor support:
|
||||
|
||||
```Python hl_lines="23 24 25"
|
||||
{!./src/dependencies/tutorial002.py!}
|
||||
```
|
||||
|
||||
<img src="/img/tutorial/dependencies/image02.png">
|
||||
|
||||
## Trees of hierarchical dependencies
|
||||
|
||||
With the **Dependency Injection** system you can build arbitrarily deep trees of hierarchical dependencies (also known as dependency graphs) by having dependencies that also have dependencies themselves.
|
||||
|
||||
You will see examples of these dependency trees in the next chapters about security.
|
||||
|
||||
## Recap
|
||||
|
||||
By using Pydantic models in your dependencies too you can keep all the editor support that **FastAPI** is designed to support.
|
||||
|
|
@ -0,0 +1,60 @@
|
|||
You can create dependencies that have sub-dependencies.
|
||||
|
||||
They can be as "deep" as you need them to be.
|
||||
|
||||
**FastAPI** will take care of solving them.
|
||||
|
||||
### First dependency "dependable"
|
||||
|
||||
You could create a first dependency ("dependable") like:
|
||||
|
||||
```Python hl_lines="6 7"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
It declares an optional query parameter `q` as a `str`, and then it just returns it.
|
||||
|
||||
This is quite simple (not very useful), but will help us focus on how the sub-dependencies work.
|
||||
|
||||
### Second dependency, "dependable" and "dependant"
|
||||
|
||||
Then you can create another dependency function (a "dependable") that at the same time declares a dependency of its own (so it is a "dependant" too):
|
||||
|
||||
```Python hl_lines="11"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
Let's focus on the parameters declared:
|
||||
|
||||
* Even though this function is a dependency ("dependable") itself, it also declares another dependency (it "depends" on something else).
|
||||
* It depends on the `query_extractor`, and assigns the value returned by it to the parameter `q`.
|
||||
* It also declares an optional `last_query` cookie, as a `str`.
|
||||
* Let's imagine that if the user didn't provide any query `q`, we just use the last query used, that we had saved to a cookie before.
|
||||
|
||||
### Use the dependency
|
||||
|
||||
Then we can use the dependency with:
|
||||
|
||||
```Python hl_lines="19"
|
||||
{!./src/dependencies/tutorial005.py!}
|
||||
```
|
||||
|
||||
!!! info
|
||||
Notice that we are only declaring one dependency in the path operation function, the `query_or_cookie_extractor`.
|
||||
|
||||
But **FastAPI** will know that it has to solve `query_extractor` first, to pass the results of that to `query_or_cookie_extractor` while calling it.
|
||||
|
||||
|
||||
## Recap
|
||||
|
||||
Apart from all the fancy words used here, the **Dependency Injection** system is quite simple.
|
||||
|
||||
Just functions that look the same as the path operation functions.
|
||||
|
||||
But still, it is very powerful, and allows you to declare arbitrarily deeply nested dependency "graphs" (trees).
|
||||
|
||||
!!! tip
|
||||
All this might not seem as useful with these simple examples.
|
||||
|
||||
But you will see how useful it is in the chapters about **security**.
|
||||
|
||||
And you will also see the amounts of code it will save you.
|
||||
10
mkdocs.yml
10
mkdocs.yml
|
|
@ -41,15 +41,17 @@ nav:
|
|||
- Custom Response: 'tutorial/custom-response.md'
|
||||
- Dependencies:
|
||||
- Dependencies Intro: 'tutorial/dependencies/intro.md'
|
||||
- First Steps: 'tutorial/dependencies/first-steps.md'
|
||||
- Second Steps: 'tutorial/dependencies/second-steps.md'
|
||||
- SQL (Relational) Databases: 'tutorial/sql-databases.md'
|
||||
- NoSQL (Distributed / Big Data) Databases: 'tutorial/nosql-databases.md'
|
||||
- First Steps - Functions: 'tutorial/dependencies/first-steps-functions.md'
|
||||
- Classes as Dependencies: 'tutorial/dependencies/classes-as-dependencies.md'
|
||||
- Sub-dependencies: 'tutorial/dependencies/sub-dependencies.md'
|
||||
- Advanced Dependencies: 'tutorial/dependencies/advanced-dependencies.md'
|
||||
- Security:
|
||||
- Security Intro: 'tutorial/security/intro.md'
|
||||
- First Steps: 'tutorial/security/first-steps.md'
|
||||
- Simple OAuth2 with Password and Bearer: 'tutorial/security/simple-oauth2.md'
|
||||
- OAuth2 with Password (and hashing), Bearer with JWT tokens: 'tutorial/security/oauth2-jwt.md'
|
||||
- SQL (Relational) Databases: 'tutorial/sql-databases.md'
|
||||
- NoSQL (Distributed / Big Data) Databases: 'tutorial/nosql-databases.md'
|
||||
- Bigger Applications - Multiple Files: 'tutorial/bigger-applications.md'
|
||||
- Application Configuration: 'tutorial/application-configuration.md'
|
||||
- Extra Starlette options: 'tutorial/extra-starlette.md'
|
||||
|
|
|
|||
Loading…
Reference in New Issue