В django я делаю свое первое приложение. Я пытаюсь найти способ поделиться объектом между несколькими методами просмотра.
например В моих views.py у меня есть
def lights_on(request):
hue_connector = HueConnector()
print(hue_connector.turn_all_lights_on())
return redirect('/')
def lights_off(request):
hue_connector = HueConnector()
print(hue_connector.turn_all_lights_off())
return redirect('/')
def light_on(request, light_id):
hue_connector = HueConnector()
hue_connector.set_light_on_state(light_id, "true")
html = "<html><body>" + light_id + "</body></html>"
return redirect('/')
Допустим, мне нужно еще 10 представлений в моем views.py, которым нужен HueConnector.
Как мне создать тот, который можно использовать всеми методами?
Что вы имеете в виду, когда говорите о совместном использовании объекта? Вы хотите избежать строки создания экземпляра hue_connector
в каждом представлении?
@ EDG956 Да, я не хочу создавать новый в каждом методе.
У вас есть несколько вариантов. Здесь я опишу два: использование одного объекта, созданного во время импорта (как предлагается в других ответах), и представления на основе классов.
Предположим, что HueConnector
может быть создан во время импорта, и это зависит только от некоторых настроек (django или внешних). Вы можете просто создать экземпляр один раз и использовать его в своих представлениях. То есть:
# app/views.py
hue_connector = HueConnector()
def lights_on(self, request):
print(hue_connector.turn_all_lights_on())
return redirect('/')
def lights_off(self, request):
print(hue_connector.turn_all_lights_off())
return redirect('/')
def light_on(self, request, light_id):
hue_connector.set_light_on_state(light_id, "true")
html = "<html><body>" + light_id + "</body></html>"
return redirect('/')
Это простой, но не потокобезопасный (хотя сделать его потокобезопасным не так уж и сложно).
Вы можете сгруппировать обработчики представлений методов http в представления на основе классов, единственная проблема заключается в том, что ваши представления, вероятно, используют общие методы HTTP, поэтому вам придется решать внутри обработчика, что делать. I:E: вызовите либо lights_on
, либо lights_off
внутри post()
в зависимости от некоторого условия.
Поскольку вы используете отдельные функции, я предполагаю, что вы используете разные URL-адреса, поэтому, немного смешивая подход DRF с сопоставлением http-метод-действие ViewSet, вы можете создать свой собственный View
класс которые понимают такое отображение.
Реализация очень голых костей может быть примерно такой (внезапно я не проверял это):
from typing import Mapping, Optional
from django.utils.decorators import classonlymethod
from django.views.generic import View
class ViewWithActions(View):
@classonlymethod
def as_view(cls, actions: Optional[Mapping[str, str]] = None, **initkwargs):
self = cls(**initkwargs)
self.setup(request, *args, **kwargs)
if not hasattr(self, "request"):
raise AttributeError(
"%s instance has no 'request' attribute. Did you override "
"setup() and forget to call super()?" % cls.__name__
)
if actions:
for method, action in actions.items():
handler = getattr(self, action)
setattr(self, method, handler)
return self.dispatch(request, *args, **kwargs)
Ключевым выводом является то, что подклассы этого класса могут определять столько методов, сколько необходимо, и позволяют программисту решать для каждого маршрута, какие методы HTTP сопоставляются с каким методом представления класса. Аргумент actions
для as_view
определяет, какая функция экземпляра класса вызывается для каждого метода http.
Вот пример того, как их использовать:
# app/views.py
from hue_connector import HueConnector # Whatever package you're using
from project.views import ViewWithActions
class LightsView(ViewWithActions):
def lights_on(self, request):
print(self.hue_connector.turn_all_lights_on())
return redirect('/')
def lights_off(self, request):
print(self.hue_connector.turn_all_lights_off())
return redirect('/')
def light_on(self, request, light_id):
self.hue_connector.set_light_on_state(light_id, "true")
html = "<html><body>" + light_id + "</body></html>"
return redirect('/')
@property
def hub_connector(self) -> HueConnector:
return HueConnector()
# app/urls.py
from django.urls import path
from app.views import LightsView
...
urlpatterns = [
...,
path("lights/on", LightsView.as_view({"post": "lights_on"})),
path("lights/off", LightsView.as_view({"post": "lights_off"})),
path("lights/<int:light_id>/on", LightsView.as_view({"post": "light_on"})),
...,
]
...
Это был бы мой предпочтительный вариант, потому что, изменив класс так, чтобы он принимал класс HueConnector (или фабрику hueconnector) в качестве зависимости, это упростило бы тестирование, передавая ему поддельные реализации во время тестов вместо исправления обезьяны.
Один из способов, который вы можете сделать, это иметь hue_connector = HueConnector()
в другом файле, скажем, utils.py
, затем импортировать его и использовать по своему усмотрению во всех видах, которые вы хотите.
# utils.py
hue_connector = HueConnector()
# views.py
from .utils import hue_connector
### Proceed using in your views
Вы можете определить глобальную переменную и получить к ней доступ из других функций.
def lights_on(request):
global hue_connector
hue_connector = HueConnector()
print(hue_connector.turn_all_lights_on())
return redirect('/')
Если вам нужно сохранить одно состояние HueConnector во всем приложении, можно сделать его (HueConnector) одноэлементным.