Django 在后台反向外键

4 投票
3 回答
11014 浏览
提问于 2025-04-18 05:07

我有一个关于Django的管理面板中外键的问题。我的情况是这样的:

class Driver(models.Model):
    name = models.CharField(max_length=200)
    executable = models.CharField(max_length=200)

class Device(models.Model):
    name = models.CharField(max_length=200)
    bound_driver = models.ForeignKey(Driver)

class DriverAssignment(models.Model):
    device = models.ForeignKey(Device)
    driver = models.ForeignKey(Driver)

每个设备都需要绑定一个驱动程序(也就是它使用的那个)。DriverAssignment应该是一个表,显示哪个驱动程序可以被哪个设备使用。所以一个设备可以绑定多个可能的驱动程序。现在我想在我的管理面板上有一个下拉菜单,显示特定设备所有可能的驱动程序,以便选择'绑定的驱动程序'。

我该如何在Django中实现这个功能呢?这对一个有经验的Django开发者来说可能很简单。我希望有人能给我一点提示,因为我对Django还比较陌生。非常感谢!

3 个回答

1

这里有一个关于反向内联的代码片段。如果你还需要,可以试试这个链接:https://gist.github.com/mzbyszewska/8b6afc312b024832aa85

我在使用django 1.5和1.6时,用过这个代码片段来处理OneToOneField。遗憾的是,我没有测试过ForeignKeyField,不过之前有用户说这个代码片段也适用于ForeignKeyField。

这个代码片段的最佳描述就在它里面。Person类对应你的DriverAssignment类,而Device则对应下面示例中的Address类:

Example:

    from django.db import models
    class Address(models.Model):
        street = models.CharField(max_length = 255)
        zipcode = models.CharField(max_length = 10)
        city = models.CharField(max_length = 255)
    class Person(models.Model):
        name = models.CharField(max_length = 255)
        business_addr = models.ForeignKey(Address,
                                             related_name = 'business_addr')
        home_addr = models.OneToOneField(Address, related_name = 'home_addr')
        other_addr = models.OneToOneField(Address, related_name = 'other_addr')



You use reverseadmin in the following way:

    from django.contrib import admin
    from django.db import models
    from models import Person
    from reverseadmin import ReverseModelAdmin
    class AddressForm(models.Form):
        pass
    class PersonAdmin(ReverseModelAdmin):
        inline_type = 'tabular'
        inline_reverse = ('business_addr', ('home_addr', AddressForm), ('other_addr' (
            'form': OtherForm
            'exclude': ()
        )))
    admin.site.register(Person, PersonAdmin)

inline_type can be either "tabular" or "stacked" for tabular and
stacked inlines respectively.
9

对于Django版本大于1.8的情况

可以使用InlineModelAdmin,具体的说明可以在那儿找到:

models.py

from django.db import models

class Author(models.Model):
   name = models.CharField(max_length=100)

class Book(models.Model):
   author = models.ForeignKey(Author, on_delete=models.CASCADE)
   title = models.CharField(max_length=100)

admin.py

from django.contrib import admin

class BookInline(admin.TabularInline):
    model = Book

class AuthorAdmin(admin.ModelAdmin):
    inlines = [
        BookInline,
    ]
2

把你的 模型结构改成这样:

class Driver(models.Model):
    name = models.CharField(max_length=200)
    executable = models.CharField(max_length=200)

class Device(models.Model):
    name = models.CharField(max_length=200)
    bound_driver = models.ForeignKey(Driver, related_name="bound_to")
    available_drivers = models.ManyToManyfield(Driver)

ManyToManyField 可以替代 DriverAssignment 表,完成同样的工作。

你可以在可用司机字段中添加可用的司机。

但你也希望 bound_driver 是可用司机之一。 这个验证你需要在表单中进行。为此,你需要重写管理界面的表单。可以参考下面的链接。

参考链接:

ManytoMany 字段: https://docs.djangoproject.com/en/1.6/ref/models/fields/#django.db.models.ManyToManyField

模型管理(重写管理功能): https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#modeladmin-objects

如果你想学得更多,就得花点时间阅读和实现这些内容。:)

或者

如果你想保持相同的结构,那么你需要重写 formModelAdmin 中,具体可以查看 这里,并提供你自己的表单,格式大概是这样的:

class CustomForm(ModelForm)
  bound_driver = forms.ModelChoiceField(queryset = <your custom queryset that returns only available drivers>, ...)
  class Meta:
    model = Device

https://docs.djangoproject.com/en/1.6/ref/contrib/admin/#django.contrib.admin.ModelAdmin.form

撰写回答