10

Biorąc pod uwagę model z polami ForeignKeyField (FKF) lub ManyToManyField (MTMF) z obcym do siebie, jak mogę zapobiec self (rekursywny) selekcji w Django Admin (admin).Jak zapobiec selekcji własnej (rekurencyjnej) dla pól FK/MTM w Django Admin

Krótko mówiąc, powinno być możliwe zapobieżenie samodzielnego (rekursywnego) wyboru instancji modelu w administratorze. Dotyczy to edycji istniejących instancji modelu, bez tworzenia nowych instancji.

Weźmy na przykład następujący model artykułu w aplikacji z wiadomościami;

class Article(models.Model):   
    title = models.CharField(max_length=100) 
    slug = models.SlugField() 
    related_articles = models.ManyToManyField('self') 

Jeśli istnieją 3 Article instancje (tytuł: A1-3), podczas edycji istniejącego Article instancji przez administratora pole related_articles reprezentowany jest domyślnie przez html (stwardnienia) Zaznacz pole, które zawiera listę WSZYSTKIE artykuły (Article.objects.all()). Użytkownik powinien widzieć i być w stanie wybrać tylko instancje inne niż one, np. Podczas edytowania Article a1, related_articles dostępne do wyboru = a2, a3.

Obecnie widzę 3 potencjalne sposoby, aby to zrobić, w kolejności malejącej preferencji;

  1. zapewnić sposób ustawić queryset zapewniając dostępnych wyborów w polu formularza administratora dla related_articles (poprzez wyłączenia filtru zapytań, np Article.objects.filter(~Q(id__iexact=self.id)) wykluczyć bieżące wystąpienie edytowany z listy related_articles użytkownik może zobaczyć i wybierz z. Tworzenie/ustawienie zestawu kwerend do użycia może wystąpić w konstruktorze (__init__) niestandardowego Article ModelForm lub, za pomocą jakiejś dynamicznej opcji limit_choices_to Model. Wymagałoby to sposobu na pobranie edytowanej instancji do użycia w celu filtrowania.
  2. Zastępuje funkcję save_model klasy Article Model lub ModelAdmin, aby sprawdzić dla i r emulować się z poziomu related_articles przed zapisaniem instancji. Wciąż oznacza to, że użytkownicy administratorzy mogą wyświetlać i wybierać wszystkie artykuły, w tym edytowaną instancję (w przypadku istniejących artykułów).
  3. Filtrowanie samodzielnych odwołań, gdy jest to wymagane do użycia poza adminem, np. szablony.

Idealne rozwiązanie (1) jest obecnie możliwe do zrobienia za pomocą niestandardowych formularzy modelowych poza adminem, ponieważ możliwe jest przekazanie przefiltrowanej zmiennej queryset dla edytowanej instancji do konstruktora formularza modelu. Pytanie brzmi: czy możesz uzyskać w instancji Article, tzn. "Self" jest edytowane przez administratora przed utworzeniem formularza, aby zrobić to samo.

Możliwe, że podchodzę do tego w niewłaściwy sposób, ale jeśli pozwolisz na zdefiniowanie FKF/MTMF dla tego samego modelu, to powinieneś mieć sposób, aby admin - zrobić właściwą rzecz: - i zapobiec użytkownik sam wybiera siebie, wykluczając go na liście dostępnych wyborów.

Uwaga: Rozwiązanie 2 i 3 są możliwe do zrobienia teraz i są wyposażone, aby spróbować uniknąć tych jak odpowiedziami Idealnie chciałbym, aby uzyskać odpowiedź na rozwiązanie 1.

Odpowiedz

2

Można użyć niestandardowy ModelForm w admin (przez ustawienie the "form" attribute of your ModelAdmin subclass). Więc robisz to w taki sam sposób, jak w każdym innym miejscu.

+0

* Technicznie *, wiem dlaczego [limit_choices_to] (https://docs.djangoproject.com/en/dev/ref/models/fields/#django.db.models.ForeignKey .limit_choices_to) nie może tego zrobić. Praktycznie nie rozumiem, dlaczego nigdy nie został wdrożony. – kojiro

+2

@kojiro - proste rzeczy powinny być proste, zaawansowane rzeczy powinny być możliwe. Nie każda możliwa funkcja musi zostać ściśnięta do limitu_wartość, właśnie dlatego, że zawsze możesz napisać własny kod ModelForm, aby robić cokolwiek chcesz. Jeśli masz sugestię dotyczącą intuicyjnej składni, aby dodać to do limitu_kolei bez komplikowania go, możesz otworzyć zgłoszenie z poprawką i zaproponować to. –

9

Carl jest prawidłowy, oto próbka wycinania i wklejania kodu, który pójdzie w admin.py

Uważam nawigowania relacje Django może być trudne, jeśli nie masz solidne zrozumienie, a żywym przykładem może być wart 1000 razy więcej niż "idź przeczytać to" (nie, że nie musisz rozumieć, co się dzieje).

class MyForm(forms.ModelForm): 
    class Meta: 
     model = MyModel 

    def __init__(self, *args, **kwargs): 
     super(MyForm, self).__init__(*args, **kwargs) 
     self.fields['myManyToManyField'].queryset = MyModel.objects.exclude(
      id__exact=self.instance.id) 
0

Można również zastąpić metodę z ModelAdmin get_form tak:

def get_form(self, request, obj=None, **kwargs): 
    """ 
    Modify the fields in the form that are self-referential by 
    removing self instance from queryset 
    """ 
    form = super().get_form(request, obj=None, **kwargs) 
    # obj won't exist yet for create page 
    if obj: 
     # Finds fieldnames of related fields whose model is self 
     rmself_fields = [f.name for f in self.model._meta.get_fields() if (
      f.concrete and f.is_relation and f.related_model is self.model)] 
     for fieldname in rmself_fields: 
      form.base_fields[fieldname]._queryset = 
       form.base_fields[fieldname]._queryset.exclude(id=obj.id) 
    return form 

Należy pamiętać, że jest to wszystko o uniwersalnym rozwiązaniem, które automatycznie wyszukuje własnym przedstawieniu pola modelowych i usuwa ja od nich wszystkich :-)

Powiązane problemy