Django optionale URL-Parameter

161

Ich habe eine Django-URL wie diese:

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

Das Problem ist, dass der project_idParameter optional sein soll.

Ich will /project_config/und /project_config/12345abdce/gleichermaßen gültiges URL - Muster sein, so dass , wenn project_id übergeben wird, dann kann ich es verwenden.

Im Moment bekomme ich eine 404, wenn ich ohne den project_idParameter auf die URL zugreife .

Darwin Tech
quelle

Antworten:

381

Es gibt verschiedene Ansätze.

Eine Möglichkeit besteht darin, eine nicht erfassende Gruppe in der Regex zu verwenden: (?:/(?P<title>[a-zA-Z]+)/)?
Erstellen eines Regex-Django-URL-Tokens Optional

Eine andere, einfacher zu befolgende Methode besteht darin, mehrere Regeln zu haben, die Ihren Anforderungen entsprechen und alle auf dieselbe Ansicht verweisen.

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

Beachten Sie, dass Sie in Ihrer Ansicht auch einen Standard für den optionalen URL-Parameter festlegen müssen, da sonst eine Fehlermeldung angezeigt wird:

def foo(request, optional_parameter=''):
    # Your code goes here
Yuji 'Tomita' Tomita
quelle
68
Stimmen Sie für die Option für mehrere Routen ab. +1
Burhan Khalid
4
@ Yuji - Kannst du das Umkehrproblem nicht lösen, indem du jedes URL-Muster benennst?
Ted
8
Können wir jeder Ansicht den gleichen Namen geben?
Eugene
2
@ Yuji'Tomita'Tomita Ich weiß, daher lautet die Antwort auf Eugenes Frage leider: Nein, wir können nicht mehrere Ansichten mit demselben Namen haben, selbst wenn wir sie implementieren, um optionale Parameter zu erhalten.
nnyby
2
@eugene Ja können wir zwei Urls mit gleichen Namen haben, Wende- abholt intelligent je nachdem , was anwendbar ist , je nach args
Arpit Singh
37

Sie können verschachtelte Routen verwenden

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

Dies ist viel trockener ( productAngenommen product_id, Sie wollten das kwarg umbenennen in , Sie müssen nur Zeile 4 ändern, und dies wirkt sich auf die folgenden URLs aus.

Bearbeitet für Django 1.8 und höher

Jacob Valenta
quelle
1
Verschachtelt ist gut. Außerdem werden verschiedene URL-Abschnitte in Ihrem Code klarer getrennt (aufgrund der Verwendung von Einrückungen)
Patrick
Das Problem mit verschachtelten Parametern besteht darin, dass Sie, wenn Sie mehrere optionale Parameter haben, nicht trocken sind, da Sie beispielsweise mit 3 optionalen Parametern 8 verschiedene Kombinationen möglicher URLs haben. Sie müssen damit umgehen, dass Parameter 1 auftritt, Parameter 1 nicht auftritt, aber Parameter 2 auftritt und Parameter 1 und 2 nicht auftreten, aber Parameter 3 auftritt. Der URL-Absatz ist VIEL schwerer zu lesen als eine einzelne Zeichenfolge mit mehreren optionalen Parametern. Die Verwendung symbolischer Konstanten für die optionalen Parametersubstrings würde das Lesen sehr einfach machen, und es würde nur eine URL geben.
Bogatyr
Ich denke, Sie haben Recht, aber das ist eher ein Ergebnis des schlechten Ansichts- / URL-Designs. Dieses Beispiel könnte überarbeitet werden, um viel besser zu sein.
Jacob Valenta
'Wohnung ist besser als verschachtelt'
pjdavis
30

Noch einfacher zu bedienen ist:

(?P<project_id>\w+|)

Das "(a | b)" bedeutet a oder b, in Ihrem Fall also ein oder mehrere Wortzeichen (\ w +) oder nichts.

So würde es aussehen:

url(
    r'^project_config/(?P<product>\w+)/(?P<project_id>\w+|)/$',
    'tool.views.ProjectConfig',
    name='project_config'
),
Juan José Brown
quelle
9
Ich mag die Einfachheit dieser Lösung, aber Vorsicht: Auf diese Weise erhält die Ansicht immer noch einen Wert für das Argument, das sein wird None. Dies bedeutet, dass Sie sich hierfür nicht auf einen Standardwert in der Signatur der Ansicht verlassen können: Sie müssen ihn explizit im Inneren testen und in der Folge zuweisen.
Anto
Dies ist, wonach ich gesucht habe =)
Mike Brian Olivera
3
Was ist mit dem letzten Schrägstrich, falls project_id nicht vorhanden ist?
Iamkhush
Sie können einfach ein hinzufügen? nach dem Schrägstrich oder einfach den Schrägstrich in das Muster project_id aufnehmen
Juan José Brown
18

Django> 2.0 Version :

Der Ansatz ist im Wesentlichen identisch mit dem in Yuji 'Tomita' Tomitas Antwort angegebenen . Betroffen ist jedoch die Syntax:

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

Mit können path()Sie auch zusätzliche Argumente an eine Ansicht mit dem optionalen Argument kwargsvom Typ übergeben dict. In diesem Fall würde Ihre Ansicht keine Standardeinstellung für das Attribut benötigen project_id:

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

Wie dies in der neuesten Django-Version gemacht wird , finden Sie in den offiziellen Dokumenten zum URL-Versand .

Jojo
quelle
1
Ich denke, Sie haben project_id und product_id in Ihrem Code verwechselt, oder?
Andreas Bergström
@ AndreasBergström vielen Dank für den Hinweis! Da hast du ganz recht! Es wurde in Eile korrigiert, wird aber später noch einmal überprüft. Hoffe es ist jetzt gut! Es gab auch das project_idnoch im Pfad im Falle der Standardeinstellung mit a dict. Dies kann zu scheinbar seltsamem Verhalten führen, da das im dictTestament angegebene Argument immer verwendet wird (wenn ich mich richtig erinnere).
Jojo
@jojo Bedeutet das, dass eine 'project_config / foo / bar' in der 2. Option automatisch die {'project_id': 'bar'} kwargs an die Ansicht übergibt?
Original BBQ Sauce
9

Ich dachte, ich würde der Antwort etwas hinzufügen.

Wenn Sie mehrere URL-Definitionen haben, müssen Sie jede einzeln benennen. Sie verlieren also die Flexibilität, wenn Sie reverse aufrufen, da ein Reverse einen Parameter erwartet, der andere nicht.

Eine andere Möglichkeit, Regex zu verwenden, um den optionalen Parameter aufzunehmen:

r'^project_config/(?P<product>\w+)/((?P<project_id>\w+)/)?$'
Tarequeh
quelle
2
In Django 1.6 ist dies eine Ausnahme für mich. Ich würde mich davon fernhaltenReverse 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
quelle
0

Verwenden ? gut funktionieren, können Sie auf Pythex überprüfen . Denken Sie daran, die Parameter * args und ** kwargs in die Definition der Ansichtsmethoden aufzunehmen

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