我如何组合我的 Django 模型,这样我就不会重复自己?

How do I combine my Django models so that I am not repeating myself?

我意识到我现在使用的 3 个模型有很多共享字段。我想知道压缩这些模型的最佳方法是什么。我已经阅读了一些关于元类和模型继承的文章,但想看看执行此操作的“最佳”方法是什么。

models.py

class Car(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    seats = models.PositiveSmallIntegerField()
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Truck(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    seats = models.PositiveSmallIntegerField()
    bed_length = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Boat(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
    length = models.CharField(max_length=100)
    width = models.CharField(max_length=100)
    HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
    current_hours = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

您可以使用 model inheritance 或模型 Mixins

具有抽象基础模型的模型继承:

class AbstractVehicle(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    
    class Meta:
        abstract = True

class Car(AbstractVehicle):
    seats = models.PositiveSmallIntegerField()    
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Truck(AbstractVehicle):
    seats = models.PositiveSmallIntegerField()
    bed_length = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Boat(AbstractVehicle):
    length = models.CharField(max_length=100)
    width = models.CharField(max_length=100)
    HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
    current_hours = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

模型混合:

class VehicleMixin(object):
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])

class Car(VehicleMixin, models.Model):
    seats = models.PositiveSmallIntegerField()    
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Truck(VehicleMixin, models.Model):
    seats = models.PositiveSmallIntegerField()
    bed_length = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)


class Boat(VehicleMixin, models.Model):
    length = models.CharField(max_length=100)
    width = models.CharField(max_length=100)
    HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
    current_hours = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

这两种解决方案都会为您留下与您的代码相同的表格。这些也只是简单的例子。您也许可以进一步改进它们。例如,您可以添加更多包含其他字段的混合或抽象基础 类。

您可以通过在车辆模型中创建 VehicleVehicleType 来组合模型,您保留所有常见车辆字段,在 VehicleType 中保留汽车、卡车、轮船等与外国-关键参考 Vehicle 模型。

class VehicleType(models.Model):
    name = models.CharField(max_length=100, unique=True)
    created = models.DateTimeField(auto_now_add=True)

class Vehicle(models.Model):
    vehicle_type = models.Foreinkey(VehicleType, on_delete=model.CASCADE)
    created = models.DateTimeField(auto_now_add=True)
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    seats = models.PositiveSmallIntegerField()
    bed_length = models.CharField(max_length=100)
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

我注意到有些字段相似但不相同,例如 year 并非所有人都相同。因此,我向您提出以下建议。

class TimeStampedModel(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True


class Vehicule(TimeStampedModel):
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

    class Meta:
        abstract = True


class WheeledVehicle(Vehicule):
    seats = models.PositiveSmallIntegerField()
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()

    class Meta:
        abstract = True


class Car(WheeledVehicle):
    year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    
    
class Truck(WheeledVehicle):
    year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    bed_length = models.CharField(max_length=100)
    

class Boat(Vehicule):
    year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
    length = models.CharField(max_length=100)
    width = models.CharField(max_length=100)
    HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
    current_hours = models.PositiveSmallIntegerField()

如果您想更进一步,我建议您添加 created_bymodified_by 字段进行审核。

class TimeStampedModel(models.Model):
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    class Meta:
        abstract = True

class TimeStampedAuthModel(TimeStampedModel):
    created_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
                                   related_name="%(app_label)s_%(class)s_created_by")
    modified_by = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, on_delete=models.SET_NULL,
                                    related_name="%(app_label)s_%(class)s_modified_by")

    class Meta:
        abstract = True


class Vehicule(TimeStampedAuthModel):
    make = models.CharField(max_length=100)
    model = models.CharField(max_length=100)
    service_interval = models.CharField(max_length=50)
    next_service = models.CharField(max_length=50)

    class Meta:
        abstract = True


class WheeledVehicle(Vehicule):
    seats = models.PositiveSmallIntegerField()
    color = models.CharField(max_length=100)
    VIN = models.CharField(max_length=17, validators=[MinLengthValidator(11)])
    current_mileage = models.PositiveSmallIntegerField()

    class Meta:
        abstract = True


class Car(WheeledVehicle):
    year = models.IntegerField(default=2021, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    
    
class Truck(WheeledVehicle):
    year = models.IntegerField(default=datetime.now().year, validators=[MinValueValidator(1886), MaxValueValidator(datetime.now().year)])
    bed_length = models.CharField(max_length=100)
    

class Boat(Vehicule):
    year = models.PositiveSmallIntegerField(default=datetime.now().year, validators=[MaxValueValidator(datetime.now().year)])
    length = models.CharField(max_length=100)
    width = models.CharField(max_length=100)
    HIN = models.CharField(max_length=14, validators=[MinLengthValidator(12)], blank=True)
    current_hours = models.PositiveSmallIntegerField()