简体   繁体   English

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

[英]django - regex for optional url parameters

I have a view in django that can accept a number of different filter parameters, but they are all optional. 我在django中有一个可以接受许多不同过滤器参数的视图,但它们都是可选的。 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? 如果我有6个可选的过滤器,我是否真的必须为6的每个组合编写URL或者有没有办法定义url的哪些部分是可选的?

To give you an example with just 2 filters, I could have all of these url possibilities: 为了给你一个只有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>/

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. 使用6个过滤器,这变得难以管理。

What's the best way to go about doing what I want to achieve? 做我想做的最好的方法是什么?

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>(?:/[^/]+/[^/]+)*)/?$',
 'views.my_view'),

Matching the required city and state is easy. 匹配所需的城市和州很容易。 The filters part is a bit more complicated. filters部分有点复杂。 The inner part - (?:/[^/]+/[^/]+)* - matches filters given in the form /name/value . 内部部分 - (?:/[^/]+/[^/]+)* - 匹配以/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. 但是, *量词(与所有Python正则表达式量词一样)只返回找到的最后一个匹配 - 所以如果url是/radius/80/company/mycompany/ only company/mycompany将被存储。 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. 请注意,正则表达式只匹配过滤器对 - 因此/company/mycompany/radius/将不匹配。 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. 例如, /fakefilter/somevalue有效。 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. filters字典中剩余的任何条目都是您可以投诉的未知值。

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: 例如, /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)

This is absolutely the use-case for GET parameters. 这绝对是GET参数的用例。 Your urlconf should just be /city/state/ , then the various filters go on the end as GET variables: 你的urlconf应该是/city/state/ ,然后各种过滤器作为GET变量结束:

/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. 现在,在您的视图中,您接受citystate作为正常参数,但其他所有内容都存储在request.GET QueryDict中。

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. 您也可以只创建一个URL(仅检查路径的起点,应该是相同的)指向您的视图,然后在视图中解析request.path 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... 另一方面,如果您在各种组合中有很多可选的过滤器参数,最好的解决方案通常是通过GET参数进行过滤,特别是如果用于过滤的网址不需要针对任何搜索引擎进行优化。 ..

尝试在urls.py中使用类似的东西:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM