【问题标题】:How do I combine my Django models so that I am not repeating myself?如何组合我的 Django 模型,以免重复自己?
【发布时间】:2021-03-16 15:48:08
【问题描述】:

我意识到我现在使用的 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)

【问题讨论】:

    标签: django django-models django-rest-framework dry


    【解决方案1】:

    我可以注意到有些字段相似但不一样,例如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()
    

    【讨论】:

    • 我也喜欢你把它分解成轮式车辆的方式。
    • 我很高兴听到这个消息。不要忘记接受答案。问候!
    【解决方案2】:

    您可以使用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)
    

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

    【讨论】:

    • 您知道这两种方法之间是否有任何优缺点?我正计划使用模型继承方法,但希望能够讨论使用 Mixins 的好处。
    • 我相信这可能会对你有所帮助:stackoverflow.com/questions/3263417/…
    【解决方案3】:

    您可以通过创建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)
    

    【讨论】:

    • 那么 Car、Truck 和 Boat 模型只有一个对 Vehicle 的 foreignKey 引用?
    • 不,这样可以让您只有一个表 Vehicle,其中每个条目可以是汽车、卡车或船。每个对象的类型将通过 vehicle_type ForeignKey 给出。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-03-04
    • 1970-01-01
    • 1970-01-01
    • 2023-03-09
    • 1970-01-01
    • 2017-04-10
    • 2011-12-12
    相关资源
    最近更新 更多