


Emulieren von Rails-ähnlichen Ressourcencontrollern in einer vom Server gerenderten Django-App
Ich mag Djangos Ansatz bei der Bearbeitung oder Weiterleitung von Anfragen nicht. Das Framework bietet zu viele Optionen und zu wenige Meinungen. Umgekehrt bieten Frameworks wie Ruby on Rails standardisierte Konventionen für die Bearbeitung und Weiterleitung von Anfragen über ihren Action Controller und das Ressourcenrouting.
In diesem Beitrag werden ViewSet und SimpleRouter des Django REST Frameworks erweitert, um eine Rails-ähnliche Anforderungshandlerklasse + Ressourcenrouting in Django-Anwendungen mit Server-Rendering bereitzustellen. Es bietet auch Methoden-Spoofing auf Formularebene für PUT-, PATCH- und DELETE-Anfragen über benutzerdefinierte Middleware.
Das Problem mit Djangos Routing
Für die Anfragebearbeitung bietet Django funktionsbasierte Ansichten, generische klassenbasierte Ansichten und modellklassenbasierte Ansichten. Die klassenbasierten Ansichten von Django verkörpern die schlimmsten Aspekte der objektorientierten Programmierung, indem sie den Kontrollfluss verschleiern und gleichzeitig erheblich mehr Code erfordern als ihre funktionsbasierten Gegenstücke.
Ebenso bietet das Framework keine Empfehlungen oder Konventionen für die URL-Pfadstruktur. Zum Vergleich hier die Konvention für eine Ruby on Rails-Ressource:
HTTP Verb | Path | Controller#Action | Used for |
---|---|---|---|
GET | /posts | posts#index | list of all posts |
GET | /posts/new | posts#new | form for creating a new post |
POST | /posts | posts#create | create a new post |
GET | /posts/:id | posts#show | display a specific post |
GET | /posts/:id/edit | posts#edit | form for editing a post |
PATCH/PUT | /posts/:id | posts#update | update a specific post |
DELETE | /posts/:id | posts#destroy | delete a specific post |
Aufgrund der Konventionen des Frameworks ist jede Ruby on Rails-App ähnlich strukturiert und neue Entwickler können sich schnell einarbeiten. Im Vergleich dazu gipfelt Djangos Laissez-faire-Ansatz in einem erheblichen Bikeshedding.
Da es keine durch das Framework erzwungenen Konventionen für Ansichten und URL-Strukturen gibt, wird jede Django-App zu einer Schneeflocke, die einen anderen Ansatz verfolgt. Schlimmer noch: Eine einzelne App kann mehrere unterschiedliche Ansätze für Ansichten und URLs verfolgen, ohne dass ein Sinn oder Grund erkennbar ist. Ich habe es gesehen. Ich habe es gelebt.
Aber das Django-Ökosystem verfügt bereits über alternative Ansätze, die Rails ähneln.
Routing des Django REST Frameworks
Im Gegensatz zu Django selbst verfügt das Django REST Framework über strenge Routing-Konventionen. Seine ViewSet-Klasse und SimpleRouter erzwingen die folgenden Konventionen:
HTTP Verb | Path | ViewSet.Action | Used for |
---|---|---|---|
GET | /posts/ | PostsViewset.list | list of all posts |
POST | /posts/ | PostsViewset.create | create a new post |
GET | /posts/:id/ | PostsViewset.retrieve | return a specific post |
PUT | /posts/:id/ | PostsViewset.update | update a specific post |
PATCH | /posts/:id/ | PostsViewset.partial_update | update part of a specific post |
DELETE | /posts/:id/ | PostsViewset.destroy | delete a specific post |
Leider funktioniert dies nur mit API-Routen. Es funktioniert nicht mit vom Django-Server gerenderten Anwendungen. Dies liegt daran, dass native Browserformulare nur GET- und POST-Anfragen implementieren können. Ruby on Rails verwendet eine versteckte Eingabe in seinen Formularen, um diese Einschränkung zu umgehen:
<form method="POST" action="/books"> <input name="title" type="text" value="My book" /> <input type="submit" /> <!-- Here's the magic part: --> <input name="_method" type="hidden" value="put" /> </form>
Bei der Übermittlung per POST-Anfrage ändert Ruby on Rails die Methode der Anfrage im Backend auf magische Weise in PUT. Django hat keine solche Funktion.
Wir können die Funktionen des Django REST Framework nutzen, um eine Rails-ähnliche Anforderungsverarbeitung und Ressourcenweiterleitung in Django zu implementieren und unsere eigene Middleware zum Überschreiben der Anforderungsmethode zu erstellen. Auf diese Weise können wir eine ähnliche Erfahrung in servergerenderten Apps erzielen, die Django-Vorlagen verwenden.
Aktionsplan
Da die ViewSet- und SimpleRouter-Klassen des Django REST Framework einen Großteil der Rails-ähnlichen Erfahrung bieten, die wir emulieren möchten, verwenden wir diese als Grundlage für unsere Implementierung. Hier ist die Routing-Struktur, die wir erstellen werden:
HTTP Verb | Path | ViewSet.Action | Used for |
---|---|---|---|
GET | /posts/ | PostsViewset.list | list of all posts |
GET | /posts/create/ | PostsViewset.create | form for creating a new post |
POST | /posts/create/ | PostsViewset.create | create a new post |
GET | /posts/:id/ | PostsViewset.retrieve | return a specific post |
GET | /posts/:id/update/ | PostsViewset.update | form for editing a post |
PUT | /posts/:id/update/ | PostsViewset.update | update a specific post |
DELETE | /posts/:id/ | PostsViewset.destroy | delete a specific post |
The routes in bold are ones that differ from what Django REST Framework's SimpleRouter provides out-of-the-box.
To build this Rails-like experience, we must do the following:
- Subclass REST Framework's ViewSet and provide it with defaults appropriate for server-rendered Django templates.
- Subclass REST Framework's SimpleRouter and create new routes for the create and update methods.
- Create a middleware that can change the request verb based on a hidden input named _method.
Prep work
We need to do a little bit of setup before we're ready to implement our routing. First, install Django REST Framework by running the following command in your main project directory:
pip install djangorestframework
Then, add REST Framework to the INSTALLED_APPS list in settings.py:
INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", # Add this: "rest_framework", ]
Next, we need a place to store our subclasses and custom middleware. Create an overrides directory in the main project directory with the following files:
overrides/ ├── __init__.py ├── middleware.py ├── routers.py └── viewsets.py
With that, we're ready to code.
Subclassing ViewSet for template rendering
Place the following code in overrides/viewsets.py:
from rest_framework.authentication import SessionAuthentication from rest_framework.parsers import FormParser from rest_framework.renderers import TemplateHTMLRenderer from rest_framework.viewsets import ViewSet class TemplateViewSet(ViewSet): authentication_classes = [SessionAuthentication] parser_classes = [FormParser] renderer_classes = [TemplateHTMLRenderer]
Our future ViewSets will be subclassed from this TemplateViewSet, and it will serve the same purpose as a Rails Action Controller. It uses the TemplateHTMLRenderer so that it renders HTML by default, the FormParser to parse form submissions, and SessionAuthentication to authenticate the user. It's nice that Django REST Framework includes these, allowing us to leverage DRF for traditional server-rendered web apps.
Subclassing SimpleRouter
The router class is what will enable us to send requests to the appropriate ViewSet method. By default, REST Framework's simple router uses POST /:resource/ to create a new resource, and PUT /:resource/:id/ to update a resource.
We must modify the create and update routes. Unlike Rails or Laravel, Django has no way to pass form errors to a redirected route. Because of this, a page containing a form to create or update a resource must post the form data to its own URL.
We will use the following routes for creating and updating resources:
- /:resource/create/ for creating a new resource.
- /:resource/:id/update/for updating a resource.
Django REST Framework's SimpleRouter has a routes list that associates the routes with the methods of the ViewSet (source code). We will subclass SimpleRouter and override its routes list, moving the create and update methods to their own routes with our desired paths.
Add the following to overrides/routers.py:
from rest_framework.routers import SimpleRouter, Route, DynamicRoute class TemplateRouter(SimpleRouter): routes = [ Route( url=r"^{prefix}{trailing_slash}$", mapping={"get": "list"}, name="{basename}-list", detail=False, initkwargs={"suffix": "List"}, ), # NEW: move "create" from the route above to its own route. Route( url=r"^{prefix}/create{trailing_slash}$", mapping={"get": "create", "post": "create"}, name="{basename}-create", detail=False, initkwargs={}, ), DynamicRoute( url=r"^{prefix}/{url_path}{trailing_slash}$", name="{basename}-{url_name}", detail=False, initkwargs={}, ), Route( url=r"^{prefix}/{lookup}{trailing_slash}$", mapping={"get": "retrieve", "delete": "destroy"}, name="{basename}-detail", detail=True, initkwargs={"suffix": "Instance"}, ), # NEW: move "update" from the route above to its own route. Route( url=r"^{prefix}/{lookup}/update{trailing_slash}$", mapping={"get": "update", "put": "update"}, name="{basename}-update", detail=True, initkwargs={}, ), DynamicRoute( url=r"^{prefix}/{lookup}/{url_path}{trailing_slash}$", name="{basename}-{url_name}", detail=True, initkwargs={}, ), ]
Overriding the HTTP method in middleware
Place the following code in overrides/middleware.py:
from django.conf import settings class FormMethodOverrideMiddleware: def __init__(self, get_response): self.get_response = get_response def __call__(self, request): if request.method == "POST": desired_method = request.POST.get("_method", "").upper() if desired_method in ("PUT", "PATCH", "DELETE"): token = request.POST.get("csrfmiddlewaretoken", "") # Override request method. request.method = desired_method # Hack to make CSRF validation pass. request.META[settings.CSRF_HEADER_NAME] = token return self.get_response(request)
If an incoming request contains a form field named _method with a value of PUT, PATCH, or DELETE, this middleware will override the request's method with its value. This allows forms to emulate other HTTP methods and have their submissions routed to the appropriate request handler.
The interesting bit of this code is the CSRF token hack. Django's middleware only checks for the csrfmiddlewaretoken form field on POST requests. However, it checks for a CSRF token on all requests with methods not defined as "safe" (any request that's not GET, HEAD, OPTIONS, or TRACE).
PUT, PATCH and DELETE requests are available through JavaScript and HTTP clients. Django expects these requests to use a CSRF token header like X-CSRFToken. Because Django will not check the the csrfmiddlewaretoken form field in non-POST requests, we must place the token in the header where the CSRF middleware will look for it.
Now that we've completed our middleware, add it to the MIDDLEWARE list in settings.py:
MIDDLEWARE = [ "django.middleware.security.SecurityMiddleware", "django.contrib.sessions.middleware.SessionMiddleware", "django.middleware.common.CommonMiddleware", "django.middleware.csrf.CsrfViewMiddleware", "django.contrib.auth.middleware.AuthenticationMiddleware", "django.contrib.messages.middleware.MessageMiddleware", "django.middleware.clickjacking.XFrameOptionsMiddleware", # Add this: "overrides.middleware.FormMethodOverrideMiddleware" ]
Using the ViewSet and Router
Let's say that we have a blog app within our Django project. Here is what the BlogPostViewSet would look like:
# blog/views.py from blog.forms import BlogPostForm from blog.models import BlogPost from django.shortcuts import get_object_or_404, render, redirect from overrides.viewsets import TemplateViewSet class BlogPostViewSet(TemplateViewSet): def list(self, request): return render(request, "blog/list.html", { "posts": BlogPost.objects.all() }) def retrieve(self, request, pk): post = get_object_or_404(BlogPost, id=pk) return render(request, "blog/retrieve.html", {"post": post}) def create(self, request): if request.method == "POST": form = BlogPostForm(request.POST) if form.is_valid(): post = form.save() return redirect(f"/posts/{post.id}/") else: form = BlogPostForm() return render(request, "blog/create.html", {"form": form}) def update(self, request, pk): post = BlogPost.objects.get(id=pk) if request.method == "PUT": form = BlogPostForm(request.POST, instance=post) if form.is_valid(): post = form.save() return redirect(f"/posts/{post.id}/") else: form = BlogPostForm(instance=post) return render(request, "blog/update.html", { "form": form, "post": post }) def destroy(self, request, pk): website = BlogPost.objects.get(id=pk) website.delete() return redirect(f"/posts/")
Here is how we would add these URLs to the project's urlpatterns list using the TemplateRouter that we created:
# project_name/urls.py from blog.views import BlogPostViewSet from django.contrib import admin from django.urls import path from overrides.routers import TemplateRouter urlpatterns = [ path("admin/", admin.site.urls), # other routes... ] router = TemplateRouter() router.register(r"posts", BlogPostViewSet, basename="post") urlpatterns += router.urls
Finally, ensure that the forms within your Django templates have both the CSRF token and hidden _method field. Here's an example from the update post form:
<form method="POST" action="/posts/{{ post.id }}/update/"> {% csrf_token %} {{ form }} <input type="hidden" name="_method" value="PUT" /> <button type="submit">Submit</button> </form>
And that's it. You now have Rails or Laravel-like controllers in your Django application.
Should you actually consider doing this?
Maybe. The advantage of this approach is that it removes a lot of opportunities for bikeshedding if your app follows REST-like conventions. If you've ever seen Adam Wathan's Cruddy by Design talk, you know that following REST-like conventions can get you pretty far.
Aber die Passform ist etwas umständlich. Rails- und Laravel-Controller verfügen über separate Endpunkte zum Anzeigen eines Formulars und zum Festschreiben einer Ressource in der Datenbank, was den Anschein erweckt, dass es eine weitere „Trennung von Belangen“ gibt, als dies mit Django möglich ist.
Das ViewSet-Modell ist auch eng mit der Idee einer „Ressource“ verbunden. Die Verwendung des TemplateViewSet wäre für eine Homepage oder Kontaktseite eine umständliche Lösung, da die Seite gezwungen wäre, die Listenmethode zu verwenden (obwohl diese in Index auf dem TemplateRouter umbenannt werden könnte). In diesen Fällen würden Sie versucht sein, eine funktionsbasierte Ansicht zu verwenden, die die Möglichkeit des Bikeshedding wieder einführt.
Schließlich machen es die automatisch generierten URL-Pfade schwieriger, auf einen Blick zu verstehen, welche Routen die Anwendung hat, ohne Tools wie Django-Erweiterungen zu verwenden.
Allerdings bietet dieser Ansatz etwas, was Django nicht bietet: strenge Konventionen mit weniger Möglichkeiten für den Fahrradabwurf. Wenn Sie das anspricht, ist dies vielleicht einen Versuch wert.
Das obige ist der detaillierte Inhalt vonEmulieren von Rails-ähnlichen Ressourcencontrollern in einer vom Server gerenderten Django-App. Für weitere Informationen folgen Sie bitte anderen verwandten Artikeln auf der PHP chinesischen Website!

Heiße KI -Werkzeuge

Undresser.AI Undress
KI-gestützte App zum Erstellen realistischer Aktfotos

AI Clothes Remover
Online-KI-Tool zum Entfernen von Kleidung aus Fotos.

Undress AI Tool
Ausziehbilder kostenlos

Clothoff.io
KI-Kleiderentferner

Video Face Swap
Tauschen Sie Gesichter in jedem Video mühelos mit unserem völlig kostenlosen KI-Gesichtstausch-Tool aus!

Heißer Artikel

Heiße Werkzeuge

Notepad++7.3.1
Einfach zu bedienender und kostenloser Code-Editor

SublimeText3 chinesische Version
Chinesische Version, sehr einfach zu bedienen

Senden Sie Studio 13.0.1
Leistungsstarke integrierte PHP-Entwicklungsumgebung

Dreamweaver CS6
Visuelle Webentwicklungstools

SublimeText3 Mac-Version
Codebearbeitungssoftware auf Gottesniveau (SublimeText3)

Heiße Themen











Python ist leichter zu lernen und zu verwenden, während C leistungsfähiger, aber komplexer ist. 1. Python -Syntax ist prägnant und für Anfänger geeignet. Durch die dynamische Tippen und die automatische Speicherverwaltung können Sie die Verwendung einfach zu verwenden, kann jedoch zur Laufzeitfehler führen. 2.C bietet Steuerung und erweiterte Funktionen auf niedrigem Niveau, geeignet für Hochleistungsanwendungen, hat jedoch einen hohen Lernschwellenwert und erfordert manuellem Speicher und Typensicherheitsmanagement.

Um die Effizienz des Lernens von Python in einer begrenzten Zeit zu maximieren, können Sie Pythons DateTime-, Zeit- und Zeitplanmodule verwenden. 1. Das DateTime -Modul wird verwendet, um die Lernzeit aufzuzeichnen und zu planen. 2. Das Zeitmodul hilft, die Studie zu setzen und Zeit zu ruhen. 3. Das Zeitplanmodul arrangiert automatisch wöchentliche Lernaufgaben.

Python ist in der Entwicklungseffizienz besser als C, aber C ist in der Ausführungsleistung höher. 1. Pythons prägnante Syntax und reiche Bibliotheken verbessern die Entwicklungseffizienz. 2. Die Kompilierungsmerkmale von Compilation und die Hardwarekontrolle verbessern die Ausführungsleistung. Bei einer Auswahl müssen Sie die Entwicklungsgeschwindigkeit und die Ausführungseffizienz basierend auf den Projektanforderungen abwägen.

Python und C haben jeweils ihre eigenen Vorteile, und die Wahl sollte auf Projektanforderungen beruhen. 1) Python ist aufgrund seiner prägnanten Syntax und der dynamischen Typisierung für die schnelle Entwicklung und Datenverarbeitung geeignet. 2) C ist aufgrund seiner statischen Tipp- und manuellen Speicherverwaltung für hohe Leistung und Systemprogrammierung geeignet.

Ist es genug, um Python für zwei Stunden am Tag zu lernen? Es hängt von Ihren Zielen und Lernmethoden ab. 1) Entwickeln Sie einen klaren Lernplan, 2) Wählen Sie geeignete Lernressourcen und -methoden aus, 3) praktizieren und prüfen und konsolidieren Sie praktische Praxis und Überprüfung und konsolidieren Sie und Sie können die Grundkenntnisse und die erweiterten Funktionen von Python während dieser Zeit nach und nach beherrschen.

PythonlistsarePartThestandardlibrary, whilearraysarenot.listarebuilt-in, vielseitig und UNDUSEDFORSPORINGECollections, während dieArrayRay-thearrayModulei und loses und loses und losesaluseduetolimitedFunctionality.

Python zeichnet sich in Automatisierung, Skript und Aufgabenverwaltung aus. 1) Automatisierung: Die Sicherungssicherung wird durch Standardbibliotheken wie OS und Shutil realisiert. 2) Skriptschreiben: Verwenden Sie die PSUTIL -Bibliothek, um die Systemressourcen zu überwachen. 3) Aufgabenverwaltung: Verwenden Sie die Zeitplanbibliothek, um Aufgaben zu planen. Die Benutzerfreundlichkeit von Python und die Unterstützung der reichhaltigen Bibliothek machen es zum bevorzugten Werkzeug in diesen Bereichen.

Zu den wichtigsten Anwendungen von Python in der Webentwicklung gehören die Verwendung von Django- und Flask -Frameworks, API -Entwicklung, Datenanalyse und Visualisierung, maschinelles Lernen und KI sowie Leistungsoptimierung. 1. Django und Flask Framework: Django eignet sich für die schnelle Entwicklung komplexer Anwendungen, und Flask eignet sich für kleine oder hochmobile Projekte. 2. API -Entwicklung: Verwenden Sie Flask oder Djangorestframework, um RESTFUFFUPI zu erstellen. 3. Datenanalyse und Visualisierung: Verwenden Sie Python, um Daten zu verarbeiten und über die Webschnittstelle anzuzeigen. 4. Maschinelles Lernen und KI: Python wird verwendet, um intelligente Webanwendungen zu erstellen. 5. Leistungsoptimierung: optimiert durch asynchrones Programmieren, Caching und Code
