พารามิเตอร์ URL ทางเลือก Django


161

ฉันมี Django URL เช่นนี้:

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

views.py:

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

ปัญหาคือฉันต้องการให้project_idพารามิเตอร์เป็นตัวเลือก

ฉันต้องการ/project_config/และ/project_config/12345abdce/เป็นรูปแบบ URL ที่ถูกต้องเท่าเทียมกันดังนั้นหาก project_idผ่านไปแล้วฉันสามารถใช้ได้

ในขณะนี้ฉันได้รับ 404 เมื่อฉันเข้าถึง URL โดยไม่มีproject_idพารามิเตอร์

คำตอบ:


381

มีหลายวิธี

หนึ่งคือการใช้กลุ่มที่ไม่ได้รับการจับภาพใน regex: การ (?:/(?P<title>[a-zA-Z]+)/)?
สร้างโทเค็น URL Django URL เป็นตัวเลือก

อีกวิธีที่ง่ายกว่าคือการมีกฎหลายข้อที่ตรงกับความต้องการของคุณทั้งหมดชี้ไปที่มุมมองเดียวกัน

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),
)

โปรดทราบว่าในมุมมองของคุณคุณจะต้องตั้งค่าเริ่มต้นสำหรับพารามิเตอร์ URL ที่เป็นตัวเลือกหรือคุณจะได้รับข้อผิดพลาด:

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

68
ลงคะแนนสำหรับตัวเลือกหลายเส้นทาง +1
Burhan Khalid

4
@Yuji - คุณไม่สามารถแก้ปัญหาการย้อนกลับได้โดยตั้งชื่อรูปแบบ URL แต่ละอัน?
Ted

8
เราจะให้ทุกชื่อดูเหมือนกันได้ไหม?
eugene

2
@ Yuji'Tomita'Tomita ฉันรู้ดังนั้นคำตอบสำหรับคำถามของ eugene นั้นน่าเสียดายที่เราไม่สามารถมีมุมมองหลายมุมมองที่มีชื่อเดียวกันแม้ว่าเราจะใช้พวกเขาเป็นวิธีรับพารามิเตอร์ทางเลือก
nnyby

2
@eugene ใช่เรามีสอง URL ที่มีชื่อเดียวกันการย้อนกลับจะเลือกอย่างชาญฉลาดขึ้นอยู่กับว่ามีอะไร
บ้าง

37

คุณสามารถใช้เส้นทางที่ซ้อนกัน

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")
        ])),
    ])),
]

นี่คือ DRY ที่มากขึ้น (สมมติว่าคุณต้องการเปลี่ยนชื่อproductkwarg เป็นproduct_idคุณเพียงแค่เปลี่ยนบรรทัดที่ 4 และจะมีผลต่อ URL ด้านล่าง

แก้ไขสำหรับ Django 1.8 ขึ้นไป


1
ซ้อนกันเป็นสิ่งที่ดี นอกจากนี้ยังแยกส่วน URL ที่แตกต่างกันในรหัสของคุณให้ชัดเจนยิ่งขึ้น (เนื่องจากการใช้การเยื้อง)
Patrick

ปัญหาของการซ้อนกันคือถ้าคุณมีพารามิเตอร์ทางเลือกหลายตัวคุณจะไม่ได้เป็น DRY เนื่องจากมีพารามิเตอร์เสริม 3 ตัวเช่นคุณมี URL ที่เป็นไปได้ 8 แบบที่ต่างกัน คุณต้องจัดการกับพารามิเตอร์ 1 ที่เกิดขึ้นพารามิเตอร์ 1 ไม่เกิดขึ้น แต่เกิดพารามิเตอร์ 2 และ 1 และ 2 ไม่เกิดขึ้น แต่พารามิเตอร์ 3 เกิดขึ้น ย่อหน้า URL จะอ่านได้ยากกว่าสตริงเดี่ยวที่มีพารามิเตอร์ทางเลือกหลายตัว การใช้ค่าคงที่เชิงสัญลักษณ์สำหรับสตริงย่อยของพารามิเตอร์ทางเลือกจะทำให้อ่านง่ายมากและจะมีเพียง URL เดียว
Bogatyr

ฉันคิดว่าคุณพูดถูก แต่นั่นเป็นผลมาจากการออกแบบมุมมอง / URL ที่ไม่ดี ตัวอย่างนี้อาจถูกทำใหม่ให้ดีขึ้นมาก
Jacob Valenta

'แฟลตดีกว่าซ้อนกัน'
pjdavis

30

ง่ายยิ่งขึ้นคือการใช้:

(?P<project_id>\w+|)

"(a | b)" หมายถึง a หรือ b ดังนั้นในกรณีของคุณมันจะเป็นอักขระคำอย่างน้อยหนึ่งตัว (\ w +) หรือไม่มีอะไรเลย

ดังนั้นมันจะมีลักษณะ:

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

9
ผมชอบความเรียบง่ายของการแก้ปัญหานี้ แต่ระวัง: Noneโดยการทำเช่นมุมมองจะยังคงได้รับความคุ้มค่าต่อการโต้แย้งซึ่งจะเป็น หมายความว่าคุณไม่สามารถพึ่งพาค่าเริ่มต้นในลายเซ็นของมุมมองสำหรับสิ่งนี้: คุณต้องทดสอบอย่างชัดเจนภายในและกำหนดผลที่ตามมา
Anto

นี่คือฉันกำลังมองหา =)
Mike Brian Olivera

3
แล้วสแลชสุดท้ายในกรณีที่ project_id ไม่มีอยู่
iamkhush

คุณสามารถเพิ่มได้หรือไม่ หลังจากสแลชหรือรวมสแลชในรูปแบบ project_id
Juan José Brown

18

Django> เวอร์ชั่น 2.0 :

วิธีการที่จะเป็นหลักที่เหมือนกันกับคนที่ได้รับในคำตอบ Yuji 'โทมิตะ' Tomita ของ อย่างไรก็ตามได้รับผลกระทบคือไวยากรณ์:

# 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
    ...

ใช้path()คุณยังสามารถส่งผ่านอาร์กิวเมนต์พิเศษเพื่อมุมมองที่มีอาร์กิวเมนต์ตัวเลือกที่เป็นชนิดkwargs dictในกรณีนี้มุมมองของคุณจะไม่ต้องการค่าเริ่มต้นสำหรับแอตทริบิวต์project_id:

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

สำหรับวิธีนี้จะทำในรุ่น Django ล่าสุดดูเอกสารอย่างเป็นทางการเกี่ยวกับ URL เยี่ยงอย่าง


1
ฉันคิดว่าคุณสับสน project_id และ product_id ในรหัสของคุณใช่ไหม
Andreas Bergström

@ AndreasBergströmขอบคุณมากสำหรับการชี้ว่า! คุณค่อนข้างถูกต้องเกี่ยวกับเรื่องนี้! แก้ไขได้อย่างรวดเร็ว แต่จะมีลักษณะที่ 2 ในภายหลัง หวังว่ามันจะโอเคตอนนี้! นอกจากนี้ยังมียังคงอยู่ในเส้นทางในกรณีของการเริ่มต้นใช้project_id dictสิ่งนี้สามารถนำไปสู่พฤติกรรมแปลก ๆ ที่ดูเหมือนจะเป็นอาร์กิวเมนต์ที่ระบุไว้ในdictจะใช้เสมอ (ถ้าฉันจำได้อย่างถูกต้อง)
jojo

@jojo นั่นหมายความว่า 'project_config / foo / bar' ในตัวเลือกที่ 2 จะผ่าน {'project_id': 'bar'} kwargs ไปยังมุมมองโดยอัตโนมัติหรือไม่
ซอสบาร์บีคิวสูตรดั้งเดิม

9

คิดว่าฉันจะเพิ่มคำตอบเล็กน้อย

หากคุณมีคำจำกัดความ URL หลายรายการคุณจะต้องตั้งชื่อให้แยกกัน ดังนั้นคุณจะสูญเสียความยืดหยุ่นเมื่อทำการโทรกลับเนื่องจากการย้อนกลับอันหนึ่งจะคาดหวังว่าพารามิเตอร์ในขณะที่อีกทางหนึ่งจะไม่

อีกวิธีในการใช้ regex เพื่อรองรับพารามิเตอร์ทางเลือก:

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

2
ใน Django 1.6 นี่ทำให้ฉันมีข้อยกเว้น ฉันจะอยู่ห่างจากมันReverse for 'edit_too_late' with arguments '()' and keyword arguments '{'pk': 128}' not found. 1 pattern(s) tried: ['orders/cannot_edit/((?P<pk>\\d+)/)?$']
แพทริค

2

Django = 2.2

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

0

ใช้งานหรือ ทำงานได้ดีคุณสามารถตรวจสอบpythex อย่าลืมเพิ่มพารามิเตอร์ * args และ ** kwargs ในคำจำกัดความของวิธีการดู

url('project_config/(?P<product>\w+)?(/(?P<project_id>\w+/)?)?', tool.views.ProjectConfig, name='project_config')
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.