1

I have a quick question about using a ForeignKey in a model.

Lets say I have models Car and EngineType. I want a Car to have EngineType as a foreign key to populate the car's engine. That is easy enough.

My problem is this: Lets say that the EngineType only should show active instances that are in production, so if they have stopped making the 3.5 liter 120 hp engine from the 1970's I don't want it to appear on the selection of engine types when you are creating a new car. The other part of this is that I want all the records to stay in the car database for historical reporting. So what happens is I try and delete the engine mentioned above and if I have

on_delete=CASCADE

this doesn't do what I want because I lose the historical data. If I have

on_delete=PROTECT

I can't delete the unwanted engine model.

How do you handle this?

Ralf
  • 16,086
  • 4
  • 44
  • 68
user3761017
  • 93
  • 2
  • 9
  • So you want to not delete the outdated `EngineType` models but just not show them in the form when creating a new `Car` right? Have you though about just making custom form that only shows `EngineType`s that are currently in production? You could add an `in_production` field to `EngineType` and then sort with that. – c6754 Sep 19 '18 at 14:53

1 Answers1

1

One option is to have your models like this (basicly what you already proposed):

class EngineType(models.Model):
    ...
    is_active = models.BooleanField(
        default=True)
    ...


class Car(models.Model):
    ...
    engine_type = models.ForeignKey(
        to=EngineType,
        on_delete=models.PROTECT)
    ...

and never delete any EngineType instances that have ever been used. Old engines would need to be marked as instance.is_active = False.

Then, in your forms and views you would need to check that only active EngineType instances may be assigned to new cars. There are some questions that address this, for example this one.

That could look like this (asuming you use ModelForm):

class CarForm(ModelForm):
    class Meta:
        model = Car
        fields = [
            ...
            'engine_type',
            ...
        ]

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.fields['engine_type'].queryset = EngineType.objects.filter(is_active=True)

This code would ensure, that only active engines can be assigned to this car through this form.

You just need to make sure that engine types cannot be changed through another view or form, or build the same checks into those too.

Ralf
  • 16,086
  • 4
  • 44
  • 68