# Developer Guide The developer guide describes how to develop apps with _django-esi_. (section-usage-in-views)= ## Usage in views ### Single token When views require a token, wrap with the `token_required` decorator and accept a `token` arg: ```python from esi.decorators import token_required @token_required(scopes="esi-characters.read_medals.v1") def my_view(request, token): # my code ``` This will prompt the user to either select a token from their current ones, or if none exist create a new one via SSO. To specify scopes, add either a list of names or a space-delimited string: ```python @token_required(scopes=['esi-location.read_ship_type.v1', 'esi-location.read_location.v1']) @token_required(scopes='esi-location.read_ship_type.v1 esi-location.read_location.v1') ``` ### New token To require a new token, such as for logging in, add the `new` argument: ```python @token_required(new=True) ``` ### Multiple tokens To request all of a user's tokens which have the required scopes, wrap instead with the `tokens_required` decorator and accept a `tokens` arg: ```python @tokens_required(scopes='esi-location.read_ship_type.v1') def my_view(request, tokens): # my code ``` This skips prompting for token selection and instead passes that responsibility to the view. Tokens are provided as a queryset. ### Single use token It is also possible to request a token for single use. Single use tokens do not require a user to be logged in and are only available to the current view. ```python from esi.decorators import single_use_token @single_use_token(scopes=['publicData']) my_view(request, token): # my code ``` :::{seealso} See API section {ref}`decorators ` for more details on all provided decorators. ::: :::{toctree} :maxdepth: 2 :caption: ESI Clients developer/openapi31 ::: ## Cleaning the database Two tasks are available: - `cleanup_callbackredirect` removes all `CallbackRedirect` models older than a specified age (in seconds). Default is 300. - `cleanup_token` checks all `Token` models, and if expired, attempts to refresh. If expired and cannot refresh, or fails to refresh, the model is deleted. _It is preferred to use the next Subset task more frequently to spread out the load on CCP._ - `cleanup_token_subset` checks a fractional subset of `Token` models, and if expired, attempts to refresh. If expired and cannot refresh, or fails to refresh, the model is deleted. To schedule these automatically with celerybeat, add them to your settings.py `CELERYBEAT_SCHEDULE` dict like so: ```python from celery.schedules import crontab CELERYBEAT_SCHEDULE = { ... 'esi_cleanup_callbackredirect': { 'task': 'esi.tasks.cleanup_callbackredirect', 'schedule': crontab(hour='*/4'), }, 'esi_cleanup_token_subset': { # 1/48th * 1hr = 48Hr/2Day Refresh Cycles. 'task': 'esi.tasks.cleanup_token_subset', 'schedule': crontab(minute="0", hour="*/1"), }, } ``` Recommended intervals are four hours for callback redirect cleanup and hourly for a fractional token cleanup (token cleanup can get quite slow with a large database, so adjust as needed). If your app does not require background token validation, it may be advantageous to not schedule the token cleanup task, instead relying on the validation check when using `@token_required` decorators or adding `.require_valid()` to the end of a query