Thông số url tùy chọn Django


161

Tôi có một URL Django như thế này:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

lượt xem:

def ProjectConfig(request, product, project_id=None, template_name='project.html'):
    ...
    # do stuff

Vấn đề là tôi muốn project_idtham số là tùy chọn.

Tôi muốn /project_config//project_config/12345abdce/là các mẫu URL hợp lệ như nhau, để nếu project_id được thông qua, thì tôi có thể sử dụng nó.

Hiện tại, tôi nhận được 404 khi tôi truy cập URL mà không có project_idtham số.

Câu trả lời:


381

Có một số cách tiếp cận.

Một là sử dụng nhóm không bắt trong regex: (?:/(?P<title>[a-zA-Z]+)/)?
Tạo mã thông báo URL Regex Django tùy chọn

Một cách khác, dễ thực hiện hơn là có nhiều quy tắc phù hợp với nhu cầu của bạn, tất cả đều hướng đến cùng một chế độ xem.

urlpatterns = patterns('',
    url(r'^project_config/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/$', views.foo),
    url(r'^project_config/(?P<product>\w+)/(?P<project_id>\w+)/$', views.foo),
)

Hãy nhớ rằng trong chế độ xem của bạn, bạn cũng cần đặt mặc định cho tham số URL tùy chọn hoặc bạn sẽ gặp lỗi:

def foo(request, optional_parameter=''):
    # Your code goes here

68
Bình chọn cho tùy chọn nhiều tuyến. +1
Burhan Khalid

4
@Yuji - bạn không thể giải quyết vấn đề đảo ngược bằng cách đặt tên cho từng mẫu url?
Ted

8
chúng ta có thể cho mọi quan điểm cùng tên không?
eugene

2
@ Yuji'Tomita'Tomita Tôi biết, vì vậy, câu trả lời cho câu hỏi của eugene là rất tiếc, không chúng ta không thể có nhiều chế độ xem với cùng một tên, ngay cả khi chúng tôi thực hiện chúng như một cách để có được các tham số tùy chọn.
nnyby

2
@eugene Có, chúng tôi có thể có hai url cùng tên, đảo ngược sẽ thông minh chọn bất cứ điều gì được áp dụng tùy thuộc vào các đối số
Arpit Singh

37

Bạn có thể sử dụng các tuyến đường lồng nhau

Django <1,8

urlpatterns = patterns(''
    url(r'^project_config/', include(patterns('',
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include(patterns('',
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ))),
    ))),
)

Django> = 1,8

urlpatterns = [
    url(r'^project_config/', include([
        url(r'^$', ProjectConfigView.as_view(), name="project_config")
        url(r'^(?P<product>\w+)$', include([
            url(r'^$', ProductView.as_view(), name="product"),
            url(r'^(?P<project_id>\w+)$', ProjectDetailView.as_view(), name="project_detail")
        ])),
    ])),
]

Đây là DRY nhiều hơn rất nhiều (Giả sử bạn muốn đổi tên productkwarg thành product_id, bạn chỉ phải thay đổi dòng 4 và nó sẽ ảnh hưởng đến các URL bên dưới.

Đã chỉnh sửa cho Django 1.8 trở lên


1
Lồng nhau là tốt. Ngoài ra, nó phân tách các phần URL khác nhau trong mã của bạn rõ ràng hơn (do sử dụng thụt lề)
Patrick

Vấn đề với lồng nhau là nếu bạn có nhiều tham số tùy chọn, thì cuối cùng bạn không phải là DRY, vì, ví dụ, với 3 tham số tùy chọn, bạn có 8 kết hợp URL khác nhau có thể. Bạn phải xử lý tham số 1 xảy ra, tham số 1 không xảy ra nhưng tham số 2 xảy ra và tham số 1 và 2 không xảy ra nhưng tham số 3 xảy ra. Đoạn URL sẽ khó đọc hơn một chuỗi với nhiều tham số tùy chọn. Sử dụng các hằng số tượng trưng cho các chuỗi tham số tùy chọn sẽ giúp bạn rất dễ đọc và sẽ chỉ có một URL.
Bogatyr

Tôi nghĩ bạn đúng, nhưng đó là kết quả của thiết kế URL / chế độ xem kém. Ví dụ này có thể được làm lại để tốt hơn nhiều.
Jacob Valenta

'Căn hộ tốt hơn lồng nhau'
pjdavis 7/07/18

30

Thậm chí đơn giản hơn là sử dụng:

(?P<project_id>\w+|)

"(A | b)" có nghĩa là a hoặc b, vì vậy trong trường hợp của bạn, nó sẽ là một hoặc nhiều ký tự từ (\ w +) hoặc không có gì.

Vì vậy, nó sẽ trông như:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),

9
Tôi thích sự đơn giản của giải pháp này, nhưng hãy cẩn thận: bằng cách đó, khung nhìn vẫn sẽ nhận được một giá trị cho đối số, sẽ là None. Có nghĩa là bạn không thể dựa vào một giá trị mặc định trong chữ ký của khung nhìn cho điều này: bạn phải kiểm tra rõ ràng nó bên trong và gán kết quả.
Anto

Đây là tôi đang tìm kiếm =)
Mike Brian Olivera

3
những gì về dấu gạch chéo cuối cùng trong trường hợp project_id không có mặt?
iamkhush

Bạn chỉ có thể thêm một? sau dấu gạch chéo hoặc chỉ bao gồm dấu gạch chéo trong mẫu project_id
Juan Jose Brown

18

Phiên bản Django> 2.0 :

Cách tiếp cận về cơ bản là giống hệt với phương pháp được đưa ra trong câu trả lời của Yuji 'Tomita' Tomita . Tuy nhiên, bị ảnh hưởng là cú pháp:

# URLconf
...

urlpatterns = [
    path(
        'project_config/<product>/',
        views.get_product, 
        name='project_config'
    ),
    path(
        'project_config/<product>/<project_id>/',
        views.get_product,
        name='project_config'
    ),
]


# View (in views.py)
def get_product(request, product, project_id='None'):
    # Output the appropriate product
    ...

Sử dụng path()bạn cũng có thể chuyển các đối số bổ sung cho chế độ xem với đối số tùy chọn kwargslà loại dict. Trong trường hợp này, chế độ xem của bạn sẽ không cần mặc định cho thuộc tính project_id:

    ...
    path(
        'project_config/<product>/',
        views.get_product,
        kwargs={'project_id': None},
        name='project_config'
    ),
    ...

Để biết cách thực hiện trong phiên bản Django gần đây nhất , hãy xem các tài liệu chính thức về việc gửi URL .


1
Tôi nghĩ rằng bạn đã trộn lẫn Project_id và product_id trong mã của mình, phải không?
Andreas Bergström

@ AndreasBergström cảm ơn rất nhiều vì đã chỉ ra điều đó! bạn hoàn toàn đúng về điều này! Sửa chữa nó vội vàng, nhưng sẽ có cái nhìn thứ 2 về nó sau. Hy vọng nó ổn bây giờ! Ngoài ra còn có project_idđường dẫn trong trường hợp mặc định sử dụng a dict. Điều này có thể dẫn đến hành vi có vẻ kỳ lạ, vì đối số được cung cấp trong di dictchúc sẽ luôn được sử dụng (nếu tôi nhớ chính xác).
jojo

@jojo Điều đó có nghĩa là 'project_config / foo / bar' trong tùy chọn thứ 2 sẽ tự động chuyển {'project_id': 'bar'} kwargs cho chế độ xem?
Sốt BBQ nguyên bản

9

Nghĩ rằng tôi sẽ thêm một chút vào câu trả lời.

Nếu bạn có nhiều định nghĩa URL thì bạn sẽ phải đặt tên riêng cho từng định nghĩa. Vì vậy, bạn mất tính linh hoạt khi gọi ngược lại vì một đảo ngược sẽ mong đợi một tham số trong khi cái kia sẽ không.

Một cách khác để sử dụng regex để chứa tham số tùy chọn:

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'

2
Trong Django 1.6, đây là một ngoại lệ đối với tôi. Tôi sẽ tránh xa nóReverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
Patrick

2

Django = 2.2

urlpatterns = [
    re_path(r'^project_config/(?:(?P<product>\w+)/(?:(?P<project_id>\w+)/)/)?$', tool.views.ProjectConfig, name='project_config')
]

0

Sử dụng ? hoạt động tốt, bạn có thể kiểm tra trên pythex . Hãy nhớ thêm các tham số * args và ** kwargs trong định nghĩa của các phương thức xem

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.