1

I have a set of models, regarding restaurants and the chefs that run them*:

class Chef(models.Model):
    name = models.TextField()

class Restaurant(models.Model):
    name = models.TextField()
    chef = models.ForeignKey(Chef)

class FrenchChef(Chef):
    angryness = models.PositiveIntegerField()

class FrenchRestaurant(Restaurant):
    region = models.TextField()

Unfortunately, this current model means a non-FrenchChef can run a FrenchRestaurant.

Is there away that I can restrict the queryset for the ForeignKey of a subbclassed model to be a subset of those available on the parent class?

* My modelling isn't actually chefs and restaurants, but this is easier to explain. It might not seem obvious, but Chefs and FrenchChefs do need to be modelled differently.

  • isn't it a question about how you organize your model not particularly in django but in any database in general. or are you asking about how to validate data .. did you look at save() function or clean data or django validation methods ?? – Vasif Feb 24 '15 at 22:54

3 Answers3

1

You could try defining clean method if you're concerned about that

class FrenchRestaurant(models.Model):
    # ...

    def clean(self):
        if not isinstance(self.chief, FrenchChief):
            raise ValidationError()
user4600699
  • 1,465
  • 11
  • 10
1

By doing this:

class FrenchChef(Chef):
    angryness = models.PositiveIntegerField()

you are creating one more table in database besides Chef. Read about types of model inheritance here: https://docs.djangoproject.com/en/1.7/topics/db/models/#model-inheritance

I think you should create one table for chefs and one table for restaurants, no inheritance needed here:

class Chef(models.Model):
    name = models.TextField()
    # all chefs with not null angryness is frenchchefs...
    # but you can add some field to explicitly save chef type
    angryness = models.PositiveIntegerField(null=True)

class Restaurant(models.Model):
    name = models.TextField()
    chef = models.ForeignKey(Chef)
    region = models.TextField()
    # rtype added here but it is not necessarily
    rtype = models.IntegerField(choices=restaurans_types)

And restriction (filtering) of choices should be in forms:

class FrenchRestaurantForm(forms.ModelForm):
    def __init__(self, *args,**kwargs):
        super (FrenchRestaurantForm, self ).__init__(*args,**kwargs)
        self.fields['chef'].queryset = Chef.objects.filter(
            angryness__gte=MIN_ANGRYNESS_LVL)
    def save(commit=True):
        model = super(FrenchRestaurantForm, self).save(commit=False)
        model.rtype = SomeFrenchRestTypeConst
        if commit:
            model.save()
        return model
    class Meta:
        model = Restaurant

To check user input you can add clean method to form field https://docs.djangoproject.com/en/1.7/ref/forms/validation/#cleaning-a-specific-field-attribute

If FrenchChef was created intentionally (it is a different table in database), then you should add it to FrenchRestaurant (another table -> another fk id):

class FrenchRestaurant(Restaurant):
    region = models.TextField()
    frenchchef = models.ForeignKey(FrenchChef)
ndpu
  • 22,225
  • 6
  • 54
  • 69
  • Unfortunately, this isn't suitable for my case. `FrenchChef`s are treated differently and are explicitly modelled as different objects. I've added a note in the question. –  Feb 24 '15 at 23:49
1

Like I was mentioning in the comment, You can look at django model data validation methods. Just found another note at this post. Adding Custom Django Model Validation

Below is a common pattern followed to do validation. code snippet is extract from one of the answers in the abouve mentioned post . answered by https://stackoverflow.com/users/247542/cerin

class BaseModel(models.Model):

    def clean(self, *args, **kwargs):
        # add custom validation here
        super(BaseModel, self).clean(*args, **kwargs)

    def save(self, *args, **kwargs):
        self.full_clean()
        super(BaseModel, self).save(*args, **kwargs)

You can go ahead and read more about validation in django documentation.

If you are looking for any type/inheretance based solutions that might exist. I am not sure if they might exist. I would still like to see if someone comes up with such provision in django.

Community
  • 1
  • 1
Vasif
  • 1,393
  • 10
  • 26