django - 可选 url 参数的正则表达式

发布于 2024-10-25 23:19:04 字数 559 浏览 6 评论 0原文

我在 django 中有一个视图可以接受许多不同的过滤器参数,但它们都是可选的。如果我有 6 个可选过滤器,我真的必须为 6 个过滤器的每个组合编写 url,还是有办法定义 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 个过滤器,这将变得难以管理。

实现我想要实现的目标的最佳方法是什么?

I have a view in django that can accept a number of different filter parameters, but they are all optional. If I have 6 optional filters, do I really have to write urls for every combination of the 6 or is there a way to define what parts of the url are optional?

To give you an example with just 2 filters, I could have all of these url possibilities:

/<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>/

All of these url's are pointing to the same view and the only required params are city and state. With 6 filters, this becomes unmanageable.

What's the best way to go about doing what I want to achieve?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吲‖鸣 2024-11-01 23:19:04

一种方法是让正则表达式将所有给定的过滤器作为单个字符串读取,然后将它们拆分为视图中的各个值。

我想出了以下 URL:

(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?

匹配所需的城市和州很容易。 filters 部分有点复杂。内部部分 - (?:/[^/]+/[^/]+)* - 匹配以 /name/value 形式给出的过滤器。但是,* 量词(与所有 Python 正则表达式量词一样)仅返回找到的最后一个匹配项 - 因此,如果 url 为 /radius/80/company/mycompany/ 则仅 < code>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)
, 'views.my_view'),

匹配所需的城市和州很容易。 filters 部分有点复杂。内部部分 - (?:/[^/]+/[^/]+)* - 匹配以 /name/value 形式给出的过滤器。但是,* 量词(与所有 Python 正则表达式量词一样)仅返回找到的最后一个匹配项 - 因此,如果 url 为 /radius/80/company/mycompany/ 则仅 < code>company/mycompany 将被存储。相反,我们告诉它不要捕获单个值(开头的 ?:),并将其放入捕获块中,该捕获块会将所有过滤器值存储为单个字符串。

视图逻辑相当简单。请注意,正则表达式仅匹配过滤器对 - 因此 /company/mycompany/radius/ 将不会匹配。这意味着我们可以安全地假设我们有成对的值。我对此进行测试的视图如下:

关于此需要注意的两件事。首先,它允许未知的过滤器条目进入您的视图。例如,/fakefilter/somevalue 是有效的。上面的视图代码忽略了这些,但您可能想向用户报告错误。如果是这样,请将获取值的代码更改为

filters 字典中剩余的任何条目都是您可以抱怨的未知值。

其次,如果用户重复过滤,则将使用最后一个值。例如,/radius/80/radius/50 会将半径设置为 50。如果您想检测到这一点,则需要在将其转换为字典之前扫描值列表:

One method would be to make the regular expression read all the given filters as a single string, and then split them up into individual values in the view.

I came up with the following URL:

(r'^(?P<city>[^/]+)/(?P<state>[^/]+)(?P<filters>(?:/[^/]+/[^/]+)*)/?

Matching the required city and state is easy. The filters part is a bit more complicated. The inner part - (?:/[^/]+/[^/]+)* - matches filters given in the form /name/value. However, the * quantifier (like all Python regular expression quantifiers) only returns the last match found - so if the url was /radius/80/company/mycompany/ only company/mycompany would be stored. Instead, we tell it not to capture the individual values (the ?: at the start), and put it inside a capturing block which will store all filter values as a single string.

The view logic is fairly straightforward. Note that the regular expression will only match pairs of filters - so /company/mycompany/radius/ will not be matched. This means we can safely assume we have pairs of values. The view I tested this with is as follows:

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)

Two things to note about this. First, it allows unknown filter entries into your view. For example, /fakefilter/somevalue is valid. The view code above ignores these, but you probably want to report an error to the user. If so, alter the code getting the values to

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

Any entries remaining in the filters dictionary are unknown values about which you can complain.

Second, if the user repeats a filter, the last value will be used. For example, /radius/80/radius/50 will set the radius to 50. If you want to detect this, you will need to scan the list of values before it is converted to a dictionary:

given = set()
for name in split[::2]:
    if name in given:
        # Repeated entry, complain to user or something.
    else:
        given.add(name)
, 'views.my_view'),

Matching the required city and state is easy. The filters part is a bit more complicated. The inner part - (?:/[^/]+/[^/]+)* - matches filters given in the form /name/value. However, the * quantifier (like all Python regular expression quantifiers) only returns the last match found - so if the url was /radius/80/company/mycompany/ only company/mycompany would be stored. Instead, we tell it not to capture the individual values (the ?: at the start), and put it inside a capturing block which will store all filter values as a single string.

The view logic is fairly straightforward. Note that the regular expression will only match pairs of filters - so /company/mycompany/radius/ will not be matched. This means we can safely assume we have pairs of values. The view I tested this with is as follows:

Two things to note about this. First, it allows unknown filter entries into your view. For example, /fakefilter/somevalue is valid. The view code above ignores these, but you probably want to report an error to the user. If so, alter the code getting the values to

Any entries remaining in the filters dictionary are unknown values about which you can complain.

Second, if the user repeats a filter, the last value will be used. For example, /radius/80/radius/50 will set the radius to 50. If you want to detect this, you will need to scan the list of values before it is converted to a dictionary:

孤独难免 2024-11-01 23:19:04

这绝对是 GET 参数的用例。您的 urlconf 应该只是 /city/state/,然后各种过滤器作为 GET 变量放在最后:

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

现在,在您看来,您接受 citystate 作为普通参数,但其他所有内容都存储在 request.GET QueryDict 中。

This is absolutely the use-case for GET parameters. Your urlconf should just be /city/state/, then the various filters go on the end as GET variables:

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

Now, in your view, you accept city and state as normal parameters, but everything else is stored in the request.GET QueryDict.

心在旅行 2024-11-01 23:19:04

您还可以只创建一个指向您的视图的 url(仅检查路径的开头,这应该是相同的),然后在您的视图中解析 request.path
另一方面,如果您确实有许多不同组合的可选过滤器参数,那么最好的解决方案通常是通过 GET 参数进行过滤,特别是如果用于过滤的 url 不需要针对任何搜索引擎进行优化...

You could also make just one url (that only checks the start of the path, that should be the same) pointing to your view and then parse request.path in your view.
On the other hand, if you have really many optional filter parameters in various combinations the best solution is very often to do th filtering via GET-parameters, especially if the urls used for filtering don't need to be optimized for any search engine...

婴鹅 2024-11-01 23:19:04

尝试在 urls.py 中使用类似的内容:

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

Try use something like that in your urls.py:

url(r'^(?P<city>[^/]+)/(?P<state>[^/]+)/(radius/(?P<miles>[^/]+)/|company/(?P<company_name>[^/]+)/)*
, 'view')
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文