Представления классов в Django

Представление Django указывает на функцию, что может стать проблемой, если вы хотите изменить лишь небольшую часть функциональности. Да, я мог бы иметь миллион аргументов ключевого слова и даже больше операторов if в функции, но я больше думал об объектно-ориентированном подходе.

Например, у меня есть страница, на которой отображается пользователь. Эта страница очень похожа на страницу, которая отображает группу, но все же не так похожа на использование другой модели данных. В группе тоже есть участники и т.д ...

Один из способов - указать представления на методы класса, а затем расширить этот класс. Кто-нибудь пробовал этот подход или есть другая идея?

Ответов (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/ )

Вы можете использовать общие представления Django. Вы можете легко достичь желаемой функциональности с помощью общих представлений Django.

Мне кажется, вы пытаетесь сочетать то, что не следует сочетать. Если вам нужно выполнить различную обработку в вашем представлении в зависимости от того, является ли вы объектом User или Group, который вы пытаетесь просмотреть, вам следует использовать две разные функции просмотра.

С другой стороны, могут быть общие идиомы, которые вы хотите извлечь из представлений типа object_detail ... возможно, вы могли бы использовать декоратор или просто вспомогательные функции?

-Дэн

Если вы просто отображаете данные из моделей, почему бы не использовать общие представления Django ? Они предназначены для того, чтобы вы могли легко отображать данные из модели без необходимости писать собственное представление и прочее о сопоставлении параметров URL-адресов с представлениями, извлечении данных, обработке крайних случаев, рендеринге вывода и т. Д.

Если вы хотите использовать общие функции между страницами, я предлагаю вам взглянуть на настраиваемые теги. Их довольно легко создать , и они очень эффективны.

Кроме того, шаблоны могут расширяться от других шаблонов . Это позволяет вам иметь базовый шаблон для настройки макета страницы и делиться им между другими шаблонами, заполняющими пробелы. Вы можете вкладывать шаблоны на любую глубину; позволяя вам определять макет на отдельных группах связанных страниц в одном месте.

Вы всегда можете создать класс, переопределить __call__функцию, а затем указать URL-файл на экземпляр класса. Вы можете взглянуть на класс FormWizard, чтобы увидеть, как это делается.

Общие представления обычно подходят, но в конечном итоге вы можете обрабатывать URL-адреса, как хотите. FormWizard работает на основе классов, как и некоторые приложения для RESTful API.

По сути, с URL-адресом вам предоставляется набор переменных и место для предоставления вызываемого объекта. Какой вызываемый объект вы предоставляете, полностью зависит от вас - стандартный способ - предоставить функцию, - но в конечном итоге Django не накладывает никаких ограничений на то, что вы делаете.

Я согласен с тем, что было бы неплохо привести еще несколько примеров того, как это сделать, хотя FormWizard, вероятно, является местом для начала.