Как создать список с динамическими c переменными - PullRequest
1 голос
/ 06 января 2020

У меня есть стандарт urlpatterns, который я использую для каждой модели, которую я хочу предоставить CRUD для:

Например, для модели Company это:

urlpatterns = [
    path('company/create', CompanyCreate.as_view(), name='company_create'),
    path('company/list', CompanyList.as_view(), name='company_list'),
    path('company/detail/<int:pk>/', CompanyDetail.as_view(), name='company_detail'),
    path('company/update/<int:pk>/', CompanyUpdate.as_view(), name='company_update'),
]

Для модели Person это:

urlpatterns = [
    path('person/create', PersonCreate.as_view(), name='person_create'),
    path('person/list', PersonList.as_view(), name='person_list'),
    path('person/detail/<int:pk>/', PersonDetail.as_view(), name='person_detail'),
    path('person/update/<int:pk>/', PersonUpdate.as_view(), name='person_update'),
]

Есть ли способ, с помощью которого можно создавать шаблоны URL-адресов, как это, просто предоставляя ввод model_name? Например,

model_list = ['company','person','car']

for model in model_list:
    urlpatterns = [
        path('%s/create', %sCreate.as_view(), name='%s_create' %model),
        path('%s/list', %sList.as_view(), name='%s_list' %model),
        path('%s/detail/<int:pk>/', %sDetail.as_view(), name='%s_detail' %model),
        path('%s/update/<int:pk>/', %sUpdate.as_view(), name='%s_update', %model),
    ]

1 Ответ

0 голосов
/ 07 января 2020

Одна идея состоит в том, чтобы динамически создавать пути. В общем, я бы рекомендовал против этого или, по крайней мере, действовать осторожно - полезно иметь возможность отслеживать, на какие URI отвечает ваше приложение, а ошибки при создании динамического пути c могут привести к раскрытию поведения, которое вы не намеревались. Но поскольку urlpatterns является чистым Python, вы можете сделать это.

Чтобы сделать это, вам нужен а) способ узнать, какие существуют виды, для которых вы хотите автоматически создавать шаблоны, и б) способ создать экземпляр path для каждого такого представления.

Для первой части один простой подход состоит в том, чтобы выполнить итерацию возвращаемого значения dir(views_module) и выполнить фильтрацию на основе некоторых критериев. Например, здесь я собираюсь предположить, что у вас все еще есть предопределенный список моделей, которые вы хотите охватить (как показано в вашем вопросе), и что любой экземпляр представления со значением атрибута model, который находится в этом списке, потенциально действителен. Вы можете извлечь выгоду из более строгих ограничений, если, скажем, у вас есть специализированные представления, которые вы не хотите включать.

Так что это что-то вроде:

from django.views.generic import CreateView, ListView, DetailView, UpdateView

import views # use your actual module here

model_list = ['company','person','car']
models_to_register = frozenset(model_list)

model_views = [possible_view for possible_view in dir(views) \
               if isinstance(possible_view, (CreateView, ListView, DetailView, UpdateView)) \
                  and getattr(possible_view, 'model', None) in models_to_register]

Тогда вам нужен способ создания path объекты из каждого вида. У вас уже будет сам объект представления, поэтому вам нужен маршрут и (необязательно, но хорошая идея) имя.

И маршрут, и имя в вашем примере построены, зная операцию, которую реализует представление, и название модели, которую охватывает представление.

Для имени у каждого вида есть атрибут model, как проверено в понимании списка, поэтому вы можете использовать свойства модели verbose_name или verbose_name_plural или определить что-то нестандартное.

Для операции я бы проверил иерархию классов общих c представлений. Так что-то вроде этого:

from django.views.generic import CreateView, ListView, DetailView, UpdateView

def operation_from_model_view(model_view):
    if isinstance(model_view, CreateView):
        return 'create'
    elif isinstance(model_view, ListView):
        return 'list'
    elif isinstance(model_view, DetailView):
        return 'detail'
    elif isinstance(model_view, EditView):
        return 'update'
    else:
        # unrecognized view type
        return None

def path_from_model_view(model_view):
    model_name = model_view.model.verbose_name
    operation = operation_from_model_view(model_view)
    if not operation or not model_name:
        # not expected for name, possible for operation
        return None
    route = '%s/%s/' % (model_name, operation)
    if operation == 'detail' or operation == 'update':
        route = route + '<int:pk>/'
    path_name = '%s_%s' % (model_name, operation)
    return path(route, model_view.as_view(), name=path_name)

Затем добавьте их в свой urlpatterns, отфильтровывая любые None s:

urlpatterns = [path for path in (path_from_model_view(view) for view in model_views) \
               if path is not None]

Или на 3.8 вы можете использовать "моржа" Оператор присваивания делает что-то вроде urlpatterns = [path for view in model_views if path := path_from_model_view(view) is not None].

Это все не проверено - могут быть синтаксические ошибки. И если у вас есть представления, которые являются экземплярами этих четырех базовых представлений в вашем модуле, которые охватывают перечисленные классы моделей, вы зарегистрируете пути к ним - я больше не помню наизусть, является ли это триггером исключения или просто перекрывает старое определение. Если у вас достаточно мало представлений, вы можете захотеть переместить путь из кода представления в собственный миксин и иметь только те представления, которые вы хотите автоматически зарегистрировать, наследуя его - это было бы ближе к явному указанию того, какие URL-адреса ваше приложение предлагает. Но если у вас достаточно мало представлений для этого, у вас может быть мало представлений, чтобы просто перечислить их вручную в вашем urls.py вместо того, чтобы делать что-либо из этого.

...