Reputation: 311
I want to make the name of a submodel unique but I can't think of a way to do it. Imagine I have the following model architecture:
class Animal(models.Model):
name = field.CharField(...)
class Meta:
abstract = False
class Panther(Animal):
class Meta:
...
class Tiger(Animal):
class Meta:
....
Now, what I want is that within the scope of the subclasses, the name of should be unique. So let's say I have a Tiger called JackTheTiger and a Panther called JackyThePanther, then no other Tiger with this name should allowed to be created and no other Panther with the name JackyThePanther should be allowed to be created.
But I want to be able to create a Tiger which is called JackyThePanther and a panther which is also called JackyThePanther. So the uniqueness should only be applied within the scope of the submodel.
I tried 2 ways to achieve what I want, both are not optimal:
Is there another way to achieve what I intend? Help is very much appreciated, thanks in advance
Upvotes: 1
Views: 188
Reputation: 397
Maybe its your solution
class Animal(models.Model):
TYPE = (
(1, 'Tyger'),
(2, 'Panter')
)
name = models.CharField(max_length=64)
type_of_animal = models.IntegerField(choices=TYPE)
class Meta:
unique_together = [['name','type_of_animal']]
In this case you can create JackyThePanther for 'Tyger' and 'Panter'. You can expand logic, for example add 'Elephant' JackyThePanther
>>> animals = Animal.objects.all()
>>> animals = Animal.objects.filter(name='JackyThePanther')
>>> animals
<QuerySet [<Animal: Animal object (1)>, <Animal: Animal object (2)>]>
Upvotes: 0
Reputation: 4849
Model inheritance in Django is rarely the best solution, and I don't think I'd turn to it in your case. You're already seeing the pitfalls it can come with in situations that aren't perfectly suited to it.
Meta.unique_together
is a model option that enables what you seem to be looking for, though you'll have to change the approach you're using:
class Species(models.Model):
name = models.CharField(...)
class Animal(models.Model):
name = models.CharField(...)
species = models.ForeignKey(Species, on_delete=models.CASCADE)
class Meta:
unique_together = [['name', 'species']]
In this case, you'd likely maintain a data migration or fixture for species, as your current architecture has them predefined in code.
Note that this uses unique_together
, which is currently valid, but the linked documentation mentions that UniqueConstrant
is likely more future-proof. I'm sticking with the former as a demonstration here since it's the one I've used.
Upvotes: 1