Parameter url opsional Django

161

Saya memiliki URL Django seperti ini:

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

Masalahnya adalah saya ingin project_idparameternya opsional.

Saya ingin /project_config/dan /project_config/12345abdce/menjadi pola URL yang sama-sama valid, sehingga jika project_id diteruskan, maka saya dapat menggunakannya.

Seperti saat ini, saya mendapatkan 404 ketika saya mengakses URL tanpa project_idparameter.

Darwin Tech
sumber

Jawaban:

381

Ada beberapa pendekatan.

Pertama adalah dengan menggunakan grup yang tidak menangkap dalam regex: (?:/(?P<title>[a-zA-Z]+)/)?
Membuat Tokex Django Regex Opsional

Cara lain yang lebih mudah diikuti adalah memiliki beberapa aturan yang sesuai dengan kebutuhan Anda, semuanya menunjuk ke tampilan yang sama.

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

Ingatlah bahwa dalam tampilan Anda, Anda juga harus menetapkan default untuk parameter URL opsional, atau Anda akan mendapatkan kesalahan:

def foo(request, optional_parameter=''):
    # Your code goes here
Yuji 'Tomita' Tomita
sumber
68
Pilih opsi multi-rute. +1
Burhan Khalid
4
@Yuji - tidak bisakah Anda menyelesaikan masalah pembalikan dengan memberi nama setiap pola url?
Ted
8
bisakah kita memberikan setiap tampilan nama yang sama?
eugene
2
@ Yuji'Tomita'Tomita Saya tahu, jadi jawaban untuk pertanyaan eugene sayangnya, tidak, kami tidak dapat dengan sehat memiliki beberapa tampilan dengan nama yang sama, bahkan jika kami menerapkannya sebagai cara untuk mendapatkan parameter opsional.
nnyby
2
@ Eugene Ya kita dapat memiliki dua url dengan nama yang sama, membalikkan akan dengan cerdas mengambil mana yang berlaku tergantung pada argumen
Arpit Singh
37

Anda dapat menggunakan rute bersarang

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

Ini jauh lebih KERING (Katakanlah Anda ingin mengganti nama productkwarg menjadi product_id, Anda hanya perlu mengubah baris 4, dan itu akan mempengaruhi URL di bawah ini.

Diedit untuk Django 1.8 dan lebih tinggi

Jacob Valenta
sumber
1
Bersarang itu bagus. Juga, ini memisahkan bagian URL yang berbeda dalam kode Anda lebih jelas (karena penggunaan indentasi)
Patrick
Masalah dengan nested adalah jika Anda memiliki beberapa parameter opsional, maka Anda akhirnya tidak KERING, karena dengan, misalnya, 3 parameter opsional, Anda memiliki 8 kombinasi berbeda dari URL yang mungkin. Anda harus menangani parameter 1 terjadi, parameter 1 tidak terjadi tetapi parameter 2 terjadi, dan parameter 1 dan 2 tidak terjadi tetapi parameter 3 terjadi. Paragraf URL akan JAUH lebih sulit dibaca daripada string tunggal dengan beberapa parameter opsional. Menggunakan konstanta simbolik untuk substring parameter opsional akan membuatnya sangat mudah dibaca, dan hanya akan ada satu URL.
Bogatyr
Saya pikir Anda benar, tetapi itu lebih merupakan hasil dari desain tampilan / URL yang buruk. Contoh ini bisa dikerjakan ulang menjadi jauh lebih baik.
Jacob Valenta
'flat lebih baik daripada bersarang'
pjdavis
30

Yang lebih sederhana adalah menggunakan:

(?P<project_id>\w+|)

"(A | b)" berarti a atau b, jadi dalam kasus Anda ini akan menjadi satu atau lebih karakter kata (\ w +) atau tidak sama sekali.

Jadi akan terlihat seperti:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),
Juan José Brown
sumber
9
Saya suka kesederhanaan dari solusi ini, tetapi berhati-hatilah: dengan melakukannya, pandangan akan tetap menerima nilai untuk argumen, yang akan menjadi None. Berarti Anda tidak dapat mengandalkan nilai default pada tanda tangan tampilan untuk ini: Anda harus mengujinya secara eksplisit di dalam dan menetapkan konsekuensinya.
Anto
Ini yang saya cari =)
Mike Brian Olivera
3
bagaimana dengan slash terakhir jika project_id tidak ada?
iamkhush
Anda bisa menambahkan? setelah slash atau sertakan saja slash dalam pola project_id
Juan José Brown
18

Django> versi 2.0 :

Pendekatan ini pada dasarnya identik dengan yang diberikan dalam Yuji 'Tomita' Tomita's Answer . Namun, yang terpengaruh adalah sintaks:

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

Menggunakan path()Anda juga bisa memberikan argumen tambahan ke tampilan dengan argumen opsional kwargsyang bertipe dict. Dalam hal ini tampilan Anda tidak perlu default untuk atribut project_id:

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

Untuk bagaimana hal ini dilakukan dalam versi Django terbaru , lihat dokumen resmi tentang pengiriman URL .

jojo
sumber
1
Saya pikir Anda mencampur project_id dan product_id dalam kode Anda, kan?
Andreas Bergström
@ AndreasBergström terima kasih banyak telah menunjukkannya! Anda benar tentang ini! Mengoreksinya dengan tergesa-gesa, tetapi akan melihat ke-2 nanti. Semoga tidak apa-apa sekarang! Ada juga yang project_idmasih ada di path jika default menggunakan a dict. Hal ini dapat menyebabkan perilaku yang tampaknya aneh, karena argumen yang diberikan di dictakan selalu digunakan (jika saya ingat dengan benar).
jojo
@ jojo Apakah itu berarti 'project_config / foo / bar' pada opsi ke-2 akan secara otomatis meneruskan {'project_id': 'bar'} kwargs ke view?
Saus BBQ Asli
9

Kupikir aku akan menambahkan sedikit ke jawabannya.

Jika Anda memiliki beberapa definisi URL maka Anda harus memberi nama masing-masing secara terpisah. Jadi, Anda kehilangan fleksibilitas saat memanggil mundur karena satu kebalikan akan mengharapkan parameter sementara yang lain tidak.

Cara lain untuk menggunakan regex untuk mengakomodasi parameter opsional:

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'
tarequeh
sumber
2
Dalam Django 1.6 ini melempar pengecualian untuk saya. Saya akan menjauh darinyaReverse 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')
]
AzizAhmad
sumber
0

Gunakan? bekerja dengan baik, Anda dapat memeriksa pythex . Ingatlah untuk menambahkan parameter * args dan ** kwargs dalam definisi metode tampilan

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