#python #django #refactoring #django-class-based-views
#python #django #рефакторинг #представления на основе классов django
Вопрос:
https://github.com/AnthonyBRoberts/fcclincoln/blob/master/apps/story/views.py
Мне немного неловко признавать, что это мое. Но это так.
class FrontpageView(DetailView):
template_name = "welcome_content.html"
def get_object(self):
return get_object_or_404(Article, slug="front-page")
def get_context_data(self, **kwargs):
context = super(FrontpageView, self).get_context_data(**kwargs)
context['slug'] = "front-page"
events = Article.objects.filter(slug="events")
context['events'] = events
return context
Итак, это довольно обычное представление на основе классов в Django.
Это назначение шаблона, получение объекта Article и добавление некоторых вещей в context_data .
Затем я скопировал этот класс 17 раз. Каждый раз в context_data добавляется другой шаблон, другой slug и другой материал.
Идея заключается в том, что есть редактор WYSIWYG, позволяющий администраторам изменять веб-контент, и система аутентификации пользователей, позволяющая нескольким людям получать доступ к содержимому сайта. По сути, суперпростая CMS, поэтому никому не нужно редактировать html для обновления сайта.
Но я действительно хотел бы провести рефакторинг, чтобы у меня не было этих почти идентичных 18 классов. Любые предложения о том, с чего мне следует начать, будут приветствоваться.
Ответ №1:
Сведите все ваши классы к одному классу, который наследует от TemplateResponseMixin
, as DetailView
does (также проверьте SingleObjectTemplateResponseMixin
) и переопределите его get_template_names()
метод, чтобы вернуть шаблон, соответствующий текущей ситуации.
Прекрасный пример этого используется в проекте django-blog-zinnia
def get_template_names(self):
"""
Return a list of template names to be used for the view.
"""
model_type = self.get_model_type()
model_name = self.get_model_name()
templates = [
'zinnia/%s/%s/entry_list.html' % (model_type, model_name),
'zinnia/%s/%s_entry_list.html' % (model_type, model_name),
'zinnia/%s/entry_list.html' % model_type,
'zinnia/entry_list.html']
if self.template_name is not None:
templates.insert(0, self.template_name)
return templates
Django возьмет этот список имен и попробует каждый элемент, чтобы увидеть, существует ли он в папке templates . Если это происходит, используется этот шаблон.
Обновить
После более пристального изучения вашего кода, возможно, что-то вроде этого:
В вашем основном urls.py
# convert each url
url(r'^$', FrontpageView.as_view()),
url(r'^history/$', HistoryView.as_view()),
url(r'^calendar/$', CalendarView.as_view()),
url(r'^news/$', NewsView.as_view()),
url(r'^visitors/$', VisitorsView.as_view()),
...
# to just
url(r'^(?P<slug>[wd/-] )/$', SuperSpecialAwesomeView.as_view()),
# but, put this at the end of urls list after any routes that don't use this view
DetailView
после установки атрибута class model
проверит, есть ли slug
в kwargs url, и если это так, он будет использовать slug для выполнения поиска модели точно так же, как то, что вы уже делаете: Article.ojects.get(slug=self.kwargs['slug'])
models.py
Вы можете добавить type
поле в свою Article
модель. Тип будет указывать, к какому типу относится статья. Например, ваши ChildrenView
, YouthView
, и AdultView
все они могут иметь тип music
(поскольку все шаблоны — это музыка, я предполагаю, что именно так они связаны).
ARTICLE_TYPE_CHOICES = (
(0, 'music'),
(1, 'weddings'),
(2, 'outreach'),
...
)
class Article(models.Model):
...
type = models.IntegerField(choices=ARTICLE_TYPE_CHOICES)
...
Затем в вашем views.py
class SuperSpecialAwesomeView(DetailView):
template_name = None
model = Article
def get_template_names(self):
slug = self.kwargs.get('slug', '')
templates = [
# create a template based on just the slug
'{0}.html'.format(slug),
# create a template based on the model's type
'{0}.html'.format(self.object.get_type_display()),
]
# Allow for template_name overrides in subclasses
if self.template_name is not None:
templates.insert(0, self.template_name)
return templates
Учитывая экземпляр статьи с типом music
и ministry/children
фрагментом , Django будет искать шаблон с именем ministry/children.html
и шаблон с именем music.html
.
И если вам нужно сделать что-то особенное для других представлений (например, вам, вероятно, понадобится для SermonsView
), тогда подкласс SuperSpecialAwesomeView
class SermonsView(SuperSpecialAwesomeView):
paginate_by = 2
queryset = Article.objects.order_by('-publish_date')
Комментарии:
1. Это фантастика, и работает точно так, как описано. Я внес некоторые изменения в функцию view, чтобы включить оператор return и get_type_display() для поиска имени типа article вместо целого числа в методе get_template_names, а также комментарий о том, куда мне нужно поместить новое регулярное выражение urls.py
Ответ №2:
Я бы подумал, что быстрый подход: добавьте поле шаблона в модель со списком предопределенных вариантов шаблонов (их можно создавать динамически). Переопределите методы DetailView по умолчанию, переопределите метод get_template_names, чтобы назначить правильный шаблон для представления (если запасной вариант недоступен, это можно сделать с помощью try: except:). Кроме того, вы можете изменить поведение представления с помощью любых флагов модели. Таким образом, вы можете иметь единую точку входа для модели, а не определять повторяющиеся представления повсюду. Я стараюсь сохранять FrontPageView независимым от других представлений, хотя для простоты и потому, что он служит другой цели. Если вам нужны повторяющиеся записи контекста, рассмотрите контекстный процессор, если вам нужны повторяющиеся записи контекста для определенных представлений, рассмотрите миксины.
Ответ №3:
Редко я могу найти места, где мне нужно использовать CBD.
Вы можете реорганизовать его следующим образом:
def editable_page(slug):
return {
'context': {
'slug': slug
}
'template': 'mysupertemplates/{0}.html'.format(slug)
}
def frontpage(req):
return editable_page('frontpage')
def chat(req):
return editable_page('char')
def about(req):
return editable_page('about')
Комментарии:
1. Почему представление на основе функций (которое не является DRY) считается рефакторингом? это подход, но не рефакторинг.
2. Это всего лишь предложение.