Django URLs: URL Variablen direkt verwenden

Sockets, TCP/IP, (XML-)RPC und ähnliche Themen gehören in dieses Forum
Antworten
killercup
User
Beiträge: 16
Registriert: Mittwoch 19. Dezember 2007, 15:58

Donnerstag 20. Dezember 2007, 16:25

Hallo,

ich bins nochmal. Nachdem meine Stats-App jetzt funktioniert (hab die Zusammenfassung heute morgen direkt mal getestet), möchte ich heute eine kleine API-App machen, die aber im Prinzip nur weiterleitet auf die API-URLs in den einzelnen anderen Apps. Zweck dessen ist, dass ich die Apps auch einzeln verwenden kann (es sind im Prinzip nur optionale Module).

Ich möchte in der URL-Config von der API-App (also api.urls) den Punkt "json/<app-name>", der automatisch die URL-Config von <app-name>.json.urls einbindet. Leider ist ja der App-Name eine URL-Variable/Parameter/wie-immer-das-heißt und ich habe leider keinen direkten Zugriff darauf. Mein Plan war folgendes:

Code: Alles auswählen

urlpatterns = patterns('',
	(r'^json/(?P<app>.*)$', include('apps.%s.json.urls' % app)),
)
Gibt es vielleicht eine andere Möglichkeit auf die Variable zuzugreifen (weitere URL-Config, Funktion, ...)? Das würde mir sehr helfen!

Danke für eure Hilfe!

Viele Grüße,

Pascal
sma
User
Beiträge: 3018
Registriert: Montag 19. November 2007, 19:57
Wohnort: Kiel

Samstag 22. Dezember 2007, 13:22

Das dein Code so nicht funktioniert, ist dir wahrscheinlich klar. Die lokale Variable "app" existiert nicht. Das $ am Ende ist wohl auch nicht, was du bezwecken willst, denn dadurch und das ".*" enthält "app" alles, was dem Präfix "json/" folgt und das ist nicht unbedingt ein gültiger Modul-Name.

Ich würde wohl genau eine Funktion benutzen, die dann selbst weiter dispatcht, in der Hoffnung, das, was dem app-Namen folgt, nicht so kompliziert ist. Das ist schnell gemacht.

Oder du schaust dir mal an, wie django.core.urlresolvers.resolve() funktioniert. Ein RegexURLResolver hat eine Methode resolve(), die probiert, ob ein übergebener Pfad passt und ein callable und Argumente oder sonst None liefert. Dabei können beliebig viele Resolver geschachtelt werden.

Bau dir dann einen passenden Resolver selbst.

Wie funktioniert es? django.conf.urls.defaults.patterns() iteriert ihre Argumente und ruft url() für jedes Tupel (es könnte auch eine Liste sein) auf. Das include() ist syntaktischer Zucker dafür, das Argument in eine Liste zu packen. url() wiederum macht dann aus dem Pattern und dem Modul-Namen in der Liste einen RegexURLResolver. Ohne include() würde es ein RegexURLPattern werden. Der Resolver speichert in urlconf_name den Namen des Moduls und hier kannst du ansetzen.

Überschreibe resolve() derart, dass wenn dein Resolver zutrifft, nicht direkt "urlconf_module" erfragt (was über __import__ "urlconf_name" lädt), sondern in der Zeile davor, die Variablen in dem Namen aus dem Match auflöst. Du kannst nicht direkt urlconf_module benutzen, dass cacht.

Code: Alles auswählen

def resolve(self, path):
  tried = []
  match = self.regex.search(path)
  if match:
    new_path = path[match.end():]
    name = self.urlconf_name % match.groupdict() # neu
    patterns = self._get_urlconf_module(name).urlpatterns # neu
    for pattern in patterns:
      ...

def _get_urlconf_module(name=None):
  if not name: return # wie die alte Implementierung
  if name in self._urlconf_modules: return self.urlconf_modules[name] #neu
  # urlconf_name laden in in neuem Cache ablegen
  ...
Stefan
killercup
User
Beiträge: 16
Registriert: Mittwoch 19. Dezember 2007, 15:58

Dienstag 25. Dezember 2007, 23:27

Wow, vielen Dank für die erstklassige Erklärung, sma!

Ich hab zwar nicht alles genau verstanden (bin Python Anfänger), aber ich denke mir so ist das gut umzusetzen. Aber als ich deine Antwort (leider eben erst) gelesen habe, kam mir eine simplere und weniger komplexe Idee, um das Ganze zu lösen.

Da ich davon ausgehen kann, das die URL "/services/json/<appname>/..." ist, nehme ich sie zu Beginn der URL-Config einfach auseinander, entferne dabei das "/services/json/" und lese dann ihren ersten Teil aus, also <appname>. Das sollte einwandfrei funktionieren und benötigt nur 4 Zeilen Code. :)

Code: Alles auswählen

try:
	app = request.path.strip('/services/json/')
	app.split('/')
	app = app[1]
Antworten