Вы можете установить внешний ключ для одной и той же модели несколько раз, но вам необходимо включить аргумент related_name
.
Все Python way
# models.py
class Status(models.Model):
slug = models.SlugField(unique=True)
category = models.TextField()
# This allows us to have heirarchy of categories.
parent = models.ForeignKey(
"self",
null=True,
blank=True,
related_name="children",
related_query_name="child",
on_delete=models.PROTECT,
)
class Example(models.Model):
category = models.ForeignKey(
Status,
related_name="example_categories",
limit_choices_to={'parent': None},
)
# This field is hidden on new objects.
sub_category = models.ForeignKey(
Status,
related_name="example_sub_categories",
null=True,
blank=True,
)
# forms.py
class ExampleForm(forms.ModelForm):
class Meta:
model = Example
fields = '__all__'
def __init__(self, *args, **kwargs):
# When we instantiate the form, we check if the object 'instance' exists.
# If it does not, we hide the `sub_category` field.
# Otherwise we restrict the queryset to children of the `category` field.
# NOTE: this will need to be properly cleaned. It does not prevent editing
# the parent `category` after the `sub_category` has already been saved.
instance = kwargs.get('instance')
if not instance:
self.fields.get('sub_category').widget = forms.HiddenInput()
else:
sub_cateogries = Status.objects.filter(parent=instance.category).all()
self.fields.get('sub_category').queryset = sub_cateogries
# admin.py
@admin.register(Example)
class ExampleAdmin(admin.ModelAdmin):
form = ExampleForm
Этот метод страдает от необходимости сохранять объект один раз, а затем иметь возможность редактировать подкатегории. Это не идеально, поскольку добавляет второй шаг к циклу управления объектами.
Лучший путь
Лучший способ справиться с этим более сложный и требует некоторого JS, поэтому Я не буду предоставлять код здесь. Однако я объясню, что это такое и как это можно сделать.
Вы можете использовать Select2 для выполнения AJAX запроса к пользовательскому представлению администратора, которое возвращает список подкатегорий в виде JSON массив.
Добавление пользовательского представления администратора достаточно просто, просто проверьте user.is_staff
и соответствующие разрешения. Вы можете добавить URL, переопределив метод get_urls()
в вашем классе ExampleAdmin
. Нечто подобное работает ...
def get_urls(self):
return [
path(
"auto_field/",
self.admin_site.admin_view(YourView.as_view()),
name='example_select2',),
] + super().get_urls()
Возможность вносить изменения такого рода является основной для возможности поднять ваш Django на следующий уровень. Первый вариант работает, но go со вторым, если можете.