Beachten Sie, dass ich untersucht habe, wie die Elemente von list_filter durch das Ergebnis eines anderen list_filter eingegrenzt werden können.
Geben Sie für das Modell, in dem die Eins-zu-Viele-Beziehung verkettet ist, das Verkettungsquellmodell in list_filter an. Zu diesem Zeitpunkt möchte ich die Elemente von list_filter entsprechend den ausgewählten Elementen reduzieren.
Betrachten Sie das folgende Modell als Beispiel, da es in Worten schwer zu verstehen ist.
Geben Sie das Geschäft und die Mitarbeiter als Listenfilter auf dem Schichtverwaltungsbildschirm an. Wenn Sie hier ein Geschäft auswählen, möchten Sie die Auswahlelemente der Mitarbeiter eingrenzen.
Überschreiben Sie die field_choices
Methode von admin.RelatedFieldListFilter
Übergeben Sie den Feldnamen und die erstellte Klasse als Taple an "list_filter".
Übergeben Sie beim Überschreiben das Argument "limit_choices_to", wenn Sie "field.get_choices" aufrufen. Der zu übergebende Wert ist ein Q-Objekt oder ein Wörterbuch.
Grenzen Sie einmal im Geschäft ein, überprüfen Sie, welche Art von Abfrage ausgegeben wird, und schränken Sie diesen Wert ein.
admin.py
from django.contrib import admin
from django.db.models import Q
from .models import Shift
class StaffFieldListFilter(admin.RelatedFieldListFilter):
def field_choices(self, field, request, model_admin):
shop_id = request.GET.get('staff__shop__id__exact')
limit_choices_to = Q(shop_id__exact=shop_id) if shop_id else None
return field.get_choices(include_blank=False, limit_choices_to=limit_choices_to)
class ShiftAdmin(admin.ModelAdmin):
list_display = (
'staff',
'start_at',
'end_at',
)
list_filter = (
'staff__shop',
('staff', StaffFieldListFilter),
)
admin.site.register(Shift, ShiftAdmin)
Bei der Auswahl eines Geschäfts wurde das Personal durch das Geschäft eingegrenzt, zu dem es gehört.
--
Wenn Sie "list_filter" in der "ModelAdmin" -Klasse definieren, werden Elemente automatisch erstellt. Überprüfen Sie daher diesen Teil.
Filtern nach django / contrib / admin / templates / admin / change_list.html Bei der Prüfung der Ausgabemethode von Stellen Sie sicher, dass das Vorlagen-Tag "admin_list_filter" verwendet wird.
Über Zeile 66
{% block filters %}
{% if cl.has_filters %}
<div id="changelist-filter">
<h2>{% trans 'Filter' %}</h2>
{% for spec in cl.filter_specs %}{% admin_list_filter cl spec %}{% endfor %}
</div>
{% endif %}
{% endblock %}
Definiert aus Zeile 418 von django / contrib / admin / templatetags / admin_list.py Wurde entdeckt.
Die Spezifikation selection (cl )
scheint die Erstellungsmethode zu sein.
Zeile 418
@register.simple_tag
def admin_list_filter(cl, spec):
tpl = get_template(spec.template)
return tpl.render({
'title': spec.title,
'choices': list(spec.choices(cl)),
'spec': spec,
})
Gehen Sie zurück zu changelist_view und sehen Sie, wie cl.filter_specs erstellt wird.
Vorbei an in der Nähe der Zeile 107 von django.contrib.admin.views.main Stellen Sie sicher, dass der list_filter in die Spezifikation eingedrückt ist.
Es scheint, dass Funktionen und Taples zusätzlich zu Zeichenfolgen an list_filter übergeben werden können.
Im Fall von tapple scheint es als "(field, field_list_filter_class)" definiert zu sein.
Bei einer Zeichenfolge wird anscheinend automatisch ein geeigneter Filter erstellt.
Spec ist also eine Instanz von field_list_filter_class
. Überprüfen wir also die selection
-Methode dieser Klasse.
Wenn Sie hier print (type (spec)) einfügen, können Sie bestimmen, welche Klasse Sie verwenden.
python:django/contrib/admin/views/main.py_l.107
if self.list_filter:
for list_filter in self.list_filter:
if callable(list_filter):
# This is simply a custom list filter class.
spec = list_filter(request, lookup_params, self.model, self.model_admin)
else:
field_path = None
if isinstance(list_filter, (tuple, list)):
# This is a custom FieldListFilter class for a given field.
field, field_list_filter_class = list_filter
else:
# This is simply a field name, so use the default
# FieldListFilter class that has been registered for
# the type of the given field.
field, field_list_filter_class = list_filter, FieldListFilter.create
if not isinstance(field, models.Field):
field_path = field
field = get_fields_from_path(self.model, field_path)[-1]
lookup_params_count = len(lookup_params)
spec = field_list_filter_class(
field, request, lookup_params,
self.model, self.model_admin, field_path=field_path
)
Die problematische Methode wurde in [Zeile 197 von django / contrib / admin / filter.py] gefunden (https://github.com/django/django/blob/44a6c27fd461e1d2f37388c26c629f8f170e8722/django/contrib/admin/filters.py#L19). Es scheint sich um eine Generatormethode zu handeln, die jeden Artikel nach der Ausgabe der Artikel zur Stornierung ausgibt ("alle"). Das Auswahlelement ist "self.lookup_choices", daher werden wir dieses als nächstes überprüfen.
python:django/contrib/admin/filters.py_l.197
def choices(self, changelist):
yield {
'selected': self.lookup_val is None and not self.lookup_val_isnull,
'query_string': changelist.get_query_string(
{},
[self.lookup_kwarg, self.lookup_kwarg_isnull]
),
'display': _('All'),
}
for pk_val, val in self.lookup_choices:
yield {
'selected': self.lookup_val == str(pk_val),
'query_string': changelist.get_query_string({
self.lookup_kwarg: pk_val,
}, [self.lookup_kwarg_isnull]),
'display': val,
}
if self.include_empty_choice:
yield {
'selected': bool(self.lookup_val_isnull),
'query_string': changelist.get_query_string({
self.lookup_kwarg_isnull: 'True',
}, [self.lookup_kwarg]),
'display': self.empty_value_display,
}
Wird von der init -Methode in derselben Klasse zugewiesen. Der Inhalt scheint die Methode field_choices
in derselben Klasse zu sein.
python:django/contrib/admin/filters.py_l.161
def __init__(self, field, request, params, model, model_admin, field_path):
other_model = get_model_from_relation(field)
self.lookup_kwarg = '%s__%s__exact' % (field_path, field.target_field.name)
self.lookup_kwarg_isnull = '%s__isnull' % field_path
self.lookup_val = request.GET.get(self.lookup_kwarg)
self.lookup_val_isnull = request.GET.get(self.lookup_kwarg_isnull)
super().__init__(field, request, params, model, model_admin, field_path)
self.lookup_choices = self.field_choices(field, request, model_admin)
Dies wird auch in derselben Klasse definiert.
Die Feldmethode get_choices
wird aufgerufen.
python:django/contrib/admin/filters.py_l.194
def field_choices(self, field, request, model_admin):
return field.get_choices(include_blank=False)
Fügen Sie hier erneut "print (Typ (Feld))" ein und überprüfen Sie die Feldklasse. Die Ausgabe ist wie folgt.
<class 'django.db.models.fields.related.ForeignKey'>
Überprüfen Sie in django / db / models / fields / related.py Die Definition der Methode get_choices konnte nicht bestätigt werden.
Es scheint, dass die Methode der geerbten Field
-Klasse verwendet wird
Definiert in Zeile 783 von django / db / models / fields / __ init__.py. Entdeckt.
python:django/db/models/fields/__init__.py_l.783
def get_choices(self, include_blank=True, blank_choice=BLANK_CHOICE_DASH, limit_choices_to=None):
"""
Return choices with a default blank choices included, for use
as <select> choices for this field.
"""
blank_defined = False
choices = list(self.choices) if self.choices else []
named_groups = choices and isinstance(choices[0][1], (list, tuple))
if not named_groups:
for choice, __ in choices:
if choice in ('', None):
blank_defined = True
break
first_choice = (blank_choice if include_blank and
not blank_defined else [])
if self.choices:
return first_choice + choices
rel_model = self.remote_field.model
limit_choices_to = limit_choices_to or self.get_limit_choices_to()
if hasattr(self.remote_field, 'get_related_field'):
lst = [(getattr(x, self.remote_field.get_related_field().attname),
smart_text(x))
for x in rel_model._default_manager.complex_filter(
limit_choices_to)]
else:
lst = [(x.pk, smart_text(x))
for x in rel_model._default_manager.complex_filter(
limit_choices_to)]
return first_choice + lst
Bestätigen Sie, dass, wenn limit_choices_to
gesetzt ist, eine Art Filter von der Methode complex_filter
angewendet zu werden scheint.
Definiert in in der Nähe der Zeile 855 von django / db / models / query.py.
In der Manager-Klasse gibt es keine Definition, aber der Definitionsspeicherort unterscheidet sich, da die Manager-Klasse über einen Prozess zum Aufrufen der Methode der QuerySet-Klasse verfügt.
python:django/db/models/query.py_l.855
def complex_filter(self, filter_obj):
"""
Return a new QuerySet instance with filter_obj added to the filters.
filter_obj can be a Q object or a dictionary of keyword lookup
arguments.
This exists to support framework features such as 'limit_choices_to',
and usually it will be more natural to use other methods.
"""
if isinstance(filter_obj, Q):
clone = self._chain()
clone.query.add_q(filter_obj)
return clone
else:
return self._filter_or_exclude(None, **filter_obj)
Wie Sie in den Kommentaren sehen können, können Sie ein Q-Objekt oder ein Wörterbuch übergeben. Also habe ich es angepasst, um dieses zu bestehen.
Recommended Posts