django - регулярное выражение для необязательных параметров URL - PullRequest
7 голосов
/ 23 марта 2011

У меня есть представление в django, которое может принимать несколько различных параметров фильтра, но все они являются необязательными.Если у меня есть 6 необязательных фильтров, действительно ли мне нужно писать URL для каждой комбинации из 6 или есть способ определить, какие части URL являются необязательными?

Чтобы дать вам пример только с 2 фильтрамиУ меня могут быть все эти URL-возможности:

/<city>/<state>/
/<city>/<state>/radius/<miles>/
/<city>/<state>/company/<company-name>/
/<city>/<state>/radius/<miles>/company/<company-name>/
/<city>/<state>/company/<company-name>/radius/<miles>/

Все эти URL-адреса указывают на одно и то же представление, и единственными необходимыми параметрами являются город и штат.С 6 фильтрами это становится неуправляемым.

Как лучше всего делать то, чего я хочу достичь?

Ответы [ 4 ]

9 голосов
/ 23 марта 2011

Одним из методов было бы заставить регулярное выражение читать все заданные фильтры как одну строку, а затем разбивать их на отдельные значения в представлении.

Я придумал следующий URL:

(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?$',
 'views.my_view'),

Соответствие необходимого города и штата легко.Часть filters немного сложнее.Внутренняя часть - (?:/[^/]+/[^/]+)* - соответствует фильтрам, указанным в форме /name/value.Однако квантификатор * (как и все квантификаторы регулярных выражений Python) возвращает только последнее найденное совпадение - поэтому, если бы URL был /radius/80/company/mycompany/, то только company/mycompany было бы сохранено.Вместо этого мы говорим ему не захватывать отдельные значения (?: в начале) и помещать его в блок захвата, который будет хранить все значения фильтра в виде одной строки.

Логика представления довольнопростой.Обратите внимание, что регулярное выражение будет соответствовать только парам фильтров - поэтому /company/mycompany/radius/ не будет совпадать.Это означает, что мы можем с уверенностью предположить, что у нас есть пары значений.Мнение, с которым я проверил это, выглядит следующим образом:

def my_view(request, city, state, filters):
    # Split into a list ['name', 'value', 'name', 'value']. Note we remove the
    # first character of the string as it will be a slash.
    split = filters[1:].split('/')

    # Map into a dictionary {'name': 'value', 'name': 'value'}.
    filters = dict(zip(split[::2], split[1::2]))

    # Get the values you want - the second parameter is the default if none was
    # given in the URL. Note all entries in the dictionary are strings at this
    # point, so you will have to convert to the appropriate types if desired.
    radius = filters.get('radius', None)
    company = filters.get('company', None)

    # Then use the values as desired in your view.
    context = {
        'city': city,
        'state': state,
        'radius': radius,
        'company': company,
    }
    return render_to_response('my_view.html', context)

Об этом следует отметить две вещи.Во-первых, он разрешает неизвестные записи фильтра в вашем представлении.Например, /fakefilter/somevalue является действительным.Код представления выше игнорирует их, но вы, вероятно, хотите сообщить об ошибке пользователю.Если это так, измените код, получая значения на

radius = filters.pop('radius', None)
company = filters.pop('company', None)

Любые записи, оставшиеся в словаре filters, являются неизвестными значениями, на которые вы можете пожаловаться.

Во-вторых, если пользователь повторяетфильтр, будет использовано последнее значение.Например, /radius/80/radius/50 установит радиус на 50. Если вы хотите обнаружить это, вам нужно будет отсканировать список значений, прежде чем он будет преобразован в словарь:

given = set()
for name in split[::2]:
    if name in given:
        # Repeated entry, complain to user or something.
    else:
        given.add(name)
8 голосов
/ 23 марта 2011

Это абсолютно случай использования параметров GET. Ваш urlconf должен быть просто /city/state/, тогда различные фильтры заканчиваются как переменные GET:

/city/state/?radius=5&company=google

Теперь, на ваш взгляд, вы принимаете city и state как обычные параметры, но все остальное хранится в request.GET QueryDict.

2 голосов
/ 23 марта 2011

Вы также можете сделать только один URL (который проверяет только начало пути, который должен быть одинаковым), указывающий на ваше представление, а затем проанализировать request.path в вашем представлении.С другой стороны, если у вас действительно много необязательных параметров фильтра в различных комбинациях, лучшим решением будет очень часто выполнять фильтрацию через GET -параметры, особенно если URL-адреса, используемые для фильтрации, не нужно оптимизировать для какого-либо поиска.двигатель ...

0 голосов
/ 23 марта 2011

Попробуйте использовать что-то подобное в своем urls.py:

url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*$', 'view')
...