您可以多次为同一个模型设置外键,但您需要包含related_name 参数。
全 Python 方式
# models.py
class Status(models.Model):
slug = models.SlugField(unique=True)
category = models.TextField()
# This allows us to have hierarchy 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 和适当的权限即可。您可以通过覆盖 ExampleAdmin 类中的 get_urls() 方法来添加 URL。像这样的工作......
def get_urls(self):
return [
path(
"auto_field/",
self.admin_site.admin_view(YourView.as_view()),
name='example_select2',),
] + super().get_urls()
能够进行这些类型的更改是让您的 Django 更上一层楼的核心。第一个选项有效,但如果可以,请选择第二个。