Представления классов в Django
Представление Django указывает на функцию, что может стать проблемой, если вы хотите изменить лишь небольшую часть функциональности. Да, я мог бы иметь миллион аргументов ключевого слова и даже больше операторов if в функции, но я больше думал об объектно-ориентированном подходе.
Например, у меня есть страница, на которой отображается пользователь. Эта страница очень похожа на страницу, которая отображает группу, но все же не так похожа на использование другой модели данных. В группе тоже есть участники и т.д ...
Один из способов - указать представления на методы класса, а затем расширить этот класс. Кто-нибудь пробовал этот подход или есть другая идея?
Ответов (9)9
Я создал и использовал свои собственные общие классы представлений, определив, __call__
что экземпляр класса может быть вызван. Мне это и вправду нравится; в то время как общие представления Django допускают некоторую настройку с помощью аргументов ключевых слов, общие представления OO (если их поведение разделено на несколько отдельных методов) могут иметь гораздо более тонкую настройку с помощью подклассов, что позволяет мне намного реже повторяться. (Я устал переписывать одну и ту же логику создания / обновления в любое время, когда мне нужно настроить что-то, что общие представления Django не позволяют).
Я разместил код на djangosnippets.org .
Единственный реальный недостаток, который я вижу, - это увеличение количества вызовов внутренних методов, что может несколько повлиять на производительность. Я не думаю, что это сильно беспокоит; очень редко выполнение кода Python может стать узким местом для производительности веб-приложения.
ОБНОВЛЕНИЕ : собственные общие представления Django теперь основаны на классах.
ОБНОВЛЕНИЕ : FWIW, я изменил свое мнение о представлениях на основе классов с тех пор, как был написан этот ответ. После того, как я широко использовал их в нескольких проектах, я чувствую, что они, как правило, приводят к коду, который достаточно СУХОЙ для написания, но его очень трудно читать и поддерживать позже, потому что функциональность распределена по очень многим разным местам, а подклассы настолько зависимы. на каждой детали реализации суперклассов и миксинов. Теперь я считаю, что TemplateResponse и декораторы представлений - лучший ответ для декомпозиции кода представления.
Мне нужно было использовать представления на основе классов, но я хотел иметь возможность использовать полное имя класса в моем URLconf без необходимости всегда создавать экземпляр класса представления перед его использованием. Мне помог удивительно простой метакласс:
class CallableViewClass(type):
def __call__(cls, *args, **kwargs):
if args and isinstance(args[0], HttpRequest):
instance = super(CallableViewClass, cls).__call__()
return instance.__call__(*args, **kwargs)
else:
instance = super(CallableViewClass, cls).__call__(*args, **kwargs)
return instance
class View(object):
__metaclass__ = CallableViewClass
def __call__(self, request, *args, **kwargs):
if hasattr(self, request.method):
handler = getattr(self, request.method)
if hasattr(handler, '__call__'):
return handler(request, *args, **kwargs)
return HttpResponseBadRequest('Method Not Allowed', status=405)
Теперь я могу создавать экземпляры классов представления и использовать экземпляры в качестве функций представления, ИЛИ я могу просто указать свой URLconf на мой класс, и метакласс создаст (и вызовет) класс представления для меня. Это работает путем проверки первого аргумента __call__
- если это, то HttpRequest
это должен быть фактический HTTP-запрос, потому что было бы бессмысленно пытаться создать экземпляр класса представления с HttpRequest
экземпляром.
class MyView(View):
def __init__(self, arg=None):
self.arg = arg
def GET(request):
return HttpResponse(self.arg or 'no args provided')
@login_required
class MyOtherView(View):
def POST(request):
pass
# And all the following work as expected.
urlpatterns = patterns(''
url(r'^myview1$', 'myapp.views.MyView', name='myview1'),
url(r'^myview2$', myapp.views.MyView, name='myview2'),
url(r'^myview3$', myapp.views.MyView('foobar'), name='myview3'),
url(r'^myotherview$', 'myapp.views.MyOtherView', name='otherview'),
)
(Я разместил отрывок об этом на http://djangosnippets.org/snippets/2041/ )
Мне кажется, вы пытаетесь сочетать то, что не следует сочетать. Если вам нужно выполнить различную обработку в вашем представлении в зависимости от того, является ли вы объектом User или Group, который вы пытаетесь просмотреть, вам следует использовать две разные функции просмотра.
С другой стороны, могут быть общие идиомы, которые вы хотите извлечь из представлений типа object_detail ... возможно, вы могли бы использовать декоратор или просто вспомогательные функции?
-Дэн
Если вы просто отображаете данные из моделей, почему бы не использовать общие представления Django ? Они предназначены для того, чтобы вы могли легко отображать данные из модели без необходимости писать собственное представление и прочее о сопоставлении параметров URL-адресов с представлениями, извлечении данных, обработке крайних случаев, рендеринге вывода и т. Д.
Если вы хотите использовать общие функции между страницами, я предлагаю вам взглянуть на настраиваемые теги. Их довольно легко создать , и они очень эффективны.
Кроме того, шаблоны могут расширяться от других шаблонов . Это позволяет вам иметь базовый шаблон для настройки макета страницы и делиться им между другими шаблонами, заполняющими пробелы. Вы можете вкладывать шаблоны на любую глубину; позволяя вам определять макет на отдельных группах связанных страниц в одном месте.
Вы всегда можете создать класс, переопределить __call__
функцию, а затем указать URL-файл на экземпляр класса. Вы можете взглянуть на класс FormWizard, чтобы увидеть, как это делается.
Общие представления обычно подходят, но в конечном итоге вы можете обрабатывать URL-адреса, как хотите. FormWizard работает на основе классов, как и некоторые приложения для RESTful API.
По сути, с URL-адресом вам предоставляется набор переменных и место для предоставления вызываемого объекта. Какой вызываемый объект вы предоставляете, полностью зависит от вас - стандартный способ - предоставить функцию, - но в конечном итоге Django не накладывает никаких ограничений на то, что вы делаете.
Я согласен с тем, что было бы неплохо привести еще несколько примеров того, как это сделать, хотя FormWizard, вероятно, является местом для начала.