Reputation: 772
I realize that the 3 models that I use right now have a ton of shared fields. I was wondering what the best way to condense these models would be. I've read some articles on metaclasses and model inheritance but wanted to see what the "best" way to do this would be.
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)
Upvotes: 0
Views: 119
Reputation: 825
I can notice that some fields are similar but aren't the same, for example year
are not the same for all. Due to that I propose to you the following.
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()
And if you want to go further, I suggest you to add created_by
and modified_by
fields to audit.
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()
Upvotes: 2
Reputation: 411
You can combine the model by creating Vehicle
and VehicleType
in vehicle model you keep all common vehicle fields, in VehicleType
keep the car, truck,boat etc with foreign-key reference to Vehicle
model.
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)
Upvotes: 2
Reputation: 514
You can use model inheritance or model Mixins
model inheritance with abstract base model:
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)
model mixins:
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)
Both of these solutions will leave you with the same tables as your code. Also these are just simple examples. You may be able to improve them further. For Example you could add even more mixins or abstract base classes that contain the other fields.
Upvotes: 2