Angenommen, ich habe Folgendes in meinem models.py
:
class Company(models.Model):
name = ...
class Rate(models.Model):
company = models.ForeignKey(Company)
name = ...
class Client(models.Model):
name = ...
company = models.ForeignKey(Company)
base_rate = models.ForeignKey(Rate)
Dh es gibt mehrere Companies
, die jeweils einen Bereich von Rates
und haben Clients
. Jeder Client
sollte eine Basis haben Rate
, die von seinem Elternteil ausgewählt wird Company's Rates
, nicht eine andere Company's Rates
.
Beim Erstellen eines Formulars zum Hinzufügen eines Client
möchte ich die Company
Auswahlmöglichkeiten entfernen (da dies bereits über die Schaltfläche "Client hinzufügen " auf der Company
Seite ausgewählt wurde) und die Rate
Auswahlmöglichkeiten Company
auch darauf beschränken.
Wie gehe ich in Django 1.0 vor?
Meine aktuelle forms.py
Datei ist im Moment nur Boilerplate:
from models import *
from django.forms import ModelForm
class ClientForm(ModelForm):
class Meta:
model = Client
Und das views.py
ist auch grundlegend:
from django.shortcuts import render_to_response, get_object_or_404
from models import *
from forms import *
def addclient(request, company_id):
the_company = get_object_or_404(Company, id=company_id)
if request.POST:
form = ClientForm(request.POST)
if form.is_valid():
form.save()
return HttpResponseRedirect(the_company.get_clients_url())
else:
form = ClientForm()
return render_to_response('addclient.html', {'form': form, 'the_company':the_company})
In Django 0.96 konnte ich dies hacken, indem ich vor dem Rendern der Vorlage Folgendes tat:
manipulator.fields[0].choices = [(r.id,r.name) for r in Rate.objects.filter(company_id=the_company.id)]
ForeignKey.limit_choices_to
scheint vielversprechend, aber ich weiß nicht, wie the_company.id
ich weitergeben soll, und ich bin mir nicht sicher , ob das außerhalb der Admin-Oberfläche trotzdem funktioniert.
Vielen Dank. (Dies scheint eine ziemlich einfache Anfrage zu sein, aber wenn ich etwas neu gestalten sollte, bin ich offen für Vorschläge.)
Antworten:
ForeignKey wird durch django.forms.ModelChoiceField dargestellt, ein ChoiceField, dessen Auswahl ein QuerySet-Modell ist. Siehe die Referenz für ModelChoiceField .
Geben Sie also ein QuerySet für das Feldattribut an
queryset
. Hängt davon ab, wie Ihr Formular erstellt wird. Wenn Sie ein explizites Formular erstellen, werden Felder direkt benannt.Wenn Sie das Standard-ModelForm-Objekt verwenden,
form.fields["rate"].queryset = ...
Dies erfolgt explizit in der Ansicht. Kein herumhacken.
quelle
__init__
Methode des Formulars festzulegen ?Zusätzlich zu S.Lotts Antwort und wie in Kommentaren erwähnt wird Guru, ist es möglich, die Abfragesatzfilter durch Überschreiben der
ModelForm.__init__
Funktion hinzuzufügen . (Dies kann leicht für normale Formulare gelten.) Es kann bei der Wiederverwendung hilfreich sein und die Ansichtsfunktion aufgeräumt halten.Dies kann nützlich sein, wenn Sie beispielsweise für viele Modelle gemeinsame Filter benötigen (normalerweise deklariere ich eine abstrakte Formularklasse). Z.B
Abgesehen davon wiederhole ich nur das Django-Blog-Material, von dem es viele gute gibt.
quelle
Dies ist einfach und funktioniert mit Django 1.4:
Sie müssen dies nicht in einer Formularklasse angeben, können dies jedoch direkt in ModelAdmin tun, da Django diese integrierte Methode bereits in ModelAdmin enthält (aus den Dokumenten):
Eine noch raffiniertere Möglichkeit, dies zu tun (z. B. beim Erstellen einer Front-End-Administrationsoberfläche, auf die Benutzer zugreifen können), besteht darin, ModelAdmin in Unterklassen zu unterteilen und anschließend die folgenden Methoden zu ändern. Das Nettoergebnis ist eine Benutzeroberfläche, die NUR Inhalte anzeigt, die sich auf sie beziehen, während Sie (ein Superuser) alles sehen können.
Ich habe vier Methoden überschrieben, die ersten beiden machen es einem Benutzer unmöglich, etwas zu löschen, und es entfernt auch die Löschschaltflächen von der Admin-Site.
Die dritte Überschreibung filtert alle Abfragen, die einen Verweis auf enthalten (im Beispiel 'Benutzer' oder 'Stachelschwein' (nur zur Veranschaulichung).
Die letzte Überschreibung filtert jedes Fremdschlüsselfeld im Modell, um die verfügbaren Auswahlmöglichkeiten genauso zu filtern wie das grundlegende Abfrageset.
Auf diese Weise können Sie eine einfach zu verwaltende, nach vorne gerichtete Administrationssite präsentieren, auf der Benutzer mit ihren eigenen Objekten herumspielen können, und Sie müssen nicht daran denken, die oben genannten spezifischen ModelAdmin-Filter einzugeben.
Schaltflächen zum Löschen entfernen:
verhindert die Löschberechtigung
Filtert Objekte, die auf der Admin-Site angezeigt werden können:
Filtert die Auswahlmöglichkeiten für alle Fremdschlüsselfelder auf der Admin-Site:
quelle
Um dies mit einer generischen Ansicht wie CreateView zu tun ...
der wichtigste Teil davon ...
, lies meinen Beitrag hier
quelle
Wenn Sie das Formular nicht erstellt haben und das Abfrageset ändern möchten, haben Sie folgende Möglichkeiten:
Dies ist sehr nützlich, wenn Sie generische Ansichten verwenden!
quelle
Ich habe wirklich versucht, das zu verstehen, aber es scheint, dass Django dies immer noch nicht sehr einfach macht. Ich bin nicht so dumm, aber ich kann einfach keine (etwas) einfache Lösung sehen.
Ich finde es im Allgemeinen ziemlich hässlich, die Admin-Ansichten für solche Dinge überschreiben zu müssen, und jedes Beispiel, das ich finde, gilt nie vollständig für die Admin-Ansichten.
Dies ist ein so häufiger Umstand bei den Modellen, die ich herstelle, dass ich es entsetzlich finde, dass es keine offensichtliche Lösung dafür gibt ...
Ich habe diese Klassen:
Dies führt zu einem Problem beim Einrichten des Administrators für das Unternehmen, da es Inlines für Vertrag und Standort enthält und die m2m-Optionen des Vertrags für den Standort nicht ordnungsgemäß nach dem Unternehmen gefiltert werden, das Sie gerade bearbeiten.
Kurz gesagt, ich würde eine Admin-Option benötigen, um so etwas zu tun:
Letztendlich wäre es mir egal, ob der Filterprozess auf der Basis CompanyAdmin oder auf der ContractInline platziert wurde. (Es ist sinnvoller, es in die Inline zu setzen, aber es macht es schwierig, den Basisvertrag als "Selbst" zu bezeichnen.)
Gibt es jemanden da draußen, der etwas weiß, das so einfach ist wie diese dringend benötigte Abkürzung? Als ich PHP-Administratoren für diese Art von Dingen machte, galt dies als Grundfunktionalität! Tatsächlich war es immer automatisch und musste deaktiviert werden, wenn Sie es wirklich nicht wollten!
quelle
Ein öffentlicherer Weg ist das Aufrufen von get_form in Admin-Klassen. Es funktioniert auch für Nicht-Datenbankfelder. Zum Beispiel habe ich hier ein Feld namens '_terminal_list' auf dem Formular, das in besonderen Fällen verwendet werden kann, um mehrere Terminalelemente aus get_list (request) auszuwählen und dann basierend auf request.user zu filtern:
quelle