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:
I create a name field for each submodel and make it unique. But then I can't query all animals and serialize the name. It also seems like bad architecture to me
I make Animal abstract. But this is no option for me since I need the database table for animal
(3. I also thought about a custom validator but I don't think it's possible because I could only validate the instances of the Supermodel)
Is there another way to achieve what I intend? Help is very much appreciated, thanks in advance
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.
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)>]>
Related
I need extend a model from another model.
Case:
core/models.py
class Master(models.Model):
code = models.CharField(max_length=30, unique=True)
name = models.CharField(max_length=100, blank=False, null=False)
class Meta:
abstract = True
class City(Master):
zipcode = models.IntegerField()
custom/models.py
from core.models import City
class City(City)
newfield = models.CharField(max_length=20)
custom is an app.
I have tried with proxy model but it is not what I need, since proxy model adds a new table. https://docs.djangoproject.com/en/2.2/topics/db/models/#proxy-models
I need is that when I migrate add the new field to City.
More info.
In core the table is created and in custom you can add new fields that the client needs. The idea is that core is only maintained as standard.
Proxy models don't add new tables. From the docs link you mentioned:
The MyPerson class operates on the same database table as its parent Person class.
If you want one table called core_city, and another called custom_city, the second one having an extra field, you simply subclass it. Perhaps it would be easier to use an alias:
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
custom_city will have all fields from core_city, plus a newfield. The description of how this works (and an example) is covered in the docs section Multi-table inheritance.
If what you want is to have one single database table, then you should use a proxy Model, however they really don't allow you to create new fields. The field should be created in the parent model, or otherwise exist in the database and not be handled by Django migrations at all.
You are looking for Abstract base classes models:
Abstract base classes are useful when you want to put some common information into a number of other models. You write your base class and put abstract=True in the Meta class.
This is the base class:
#core/models.py
class City(Master):
zipcode = models.IntegerField()
class Meta:
abstract = True # <--- here the trick
Here your model:
#custom/models.py
from core.models import City as CoreCity
class City(CoreCity):
newfield = models.CharField(max_length=20)
For many uses, this type of model inheritance will be exactly what you want. It provides a way to factor out common information at the Python level, while still only creating one database table per child model at the database level.
You can update or create your class constants after its defined like this
from core.models import City
City.newfield = models.CharField(max_length=20)
You may need to use swappable models, using them you can define a City class and change it with whichever model you need later,
but that way you can't import and use the base City model directly, you may need to provide a method like get_city_model for that, as your public API.
class City(Master):
zipcode = models.IntegerField()
class Meta:
swappable = 'CORE_CITY_MODEL'
and maybe replace it later with some other model, then just set CORE_CITY_MODEL to that model in the form of 'app_name.model_name'.
The django.contrib.auth is a good example of this, you may consider checking User model and get_user_model method. Although I think you may face problems if you change your city model after you did run migrate, it may not move your data to the new table, but I'm not sure about this.
So, in our database we had a Car model, but there could be different kinds of cars, with different fields and different processes associated to them, so I made some model classes that inherit from that Car, it's something like this:
class Car(models.Model):
field = models.CharField(max_length=300)
class CarA(Car):
field_a = models.CharField(max_length=300)
class CarA1(CarA):
field_a1 = models.CharField(max_length=300)
class CarA2(CarA):
field_a2 = models.CharField(max_length=300)
class CarB(Car):
field_b = models.CharField(max_length=300)
Also, there are trips, and each trip has a car associated to them:
class Trip(BaseModel):
car = models.ForeignKey(Car)
If I have a CarA1 instance, car_a1, I can save it in a Trip with car_a1.car_ptr, but it would be saved as a Car, and from there I don't think there's a way to get the CarA1 instance again except searching by the Car id in all the subclasses.
So, if I want to know which kind of car the trip has, or get a specific subclass field, it would be really complicated.
What do you suggest to do? I can just keep only the Car class with the specific fields nullable (field_a, field_b, etc), and a field to specify the kind of car, but that doesn't seem right, I would have to check on the kind to validate the fields, etc. But right now it seems like the better solution.
I'm using Django 1.11 and Python 3.6 btw.
Sorry I didn't use real code, I thought it would be way more complicated, also sorry if I didn't explain it very well, I'm sleep deprived and stressed.
Thanks!
The problem in your model is when a new type of car introduced say car c you need to create another table for that. It is not ideal and what you need is one table for store the car details(commonly used) and separate table say Properties to store other details and give a foreign-key relation to Car model.
First, you can get to the child model from the parent model (see model based inheritance).
If you want to stick with your inheritance based design, you could define a field in Car called car_type which would correspond to the type of the child table. For example:
class Car(models.Model):
CAR_A = 'car_a'
CAR_B = 'car_b'
CAR_TYPE_CHOICES = (
(CAR_A, 'Car_A'),
(CAR_B, 'Car_b'),
)
field = models.CharField(max_length=300)
car_type = models.CharField(max_length=20, choices=CAR_TYPE_CHOICES)
And then you could override the save method of the child models so that their car_type field is the one that corresponds to their model.
class CarA(Car):
field_a = models.CharField(max_length=300)
def save(self, *args, **kwargs):
self.car_type = Car.CAR_A
super(CarA, self).save(*args, **kwargs)
# ... similarly for CarB
Now you can get to the child model from Car based on its car_type.
if car.car_type === Car.CAR_A:
print(car.cara.field_a)
Note that this will start to get cumbersome if you have many levels of inheritance, so I would encourage you to reconsider your design.
I have a situation where I need to be able to add multiple copies of the same object to a many-to-many relationship.
Let's say that the problem is recording the types of furniture someone has. Here are my base models:
class Person(models.Model):
name = models.CharField(max_length=100)
class Furniture(models.Model):
furniture_name = models.CharField(max_length=100) #e.g. Chair, Sofa.
Lets say I want to record that Sam owns 3 chairs and 2 sofas. But I don't want to make more than one sofa object in the DB. How can I do this with a many-to-many relationship? The traditional many-to-many has a unique-constraint that prevents this.
I'm thinking of using a through table, with another field in the unique-constraint (date-purchased, or just a random string). Will that work?
Does anyone have a better way of doing this?
Creating a through table is a good approach here, as everything will be DRY and pretty easy to use:
class Ownership:
owner = models.ForeignKey(Person, on_delete=models.CASCADE)
furniture = models.ForeignKey(Furniture, on_delete=models.CASCADE)
items_owned = models.IntegerField(default=0)
class Meta:
unique_together = [
'owner', 'furniture'
]
person = Person.objects.get(name='Bob')
furniture = Furniture.objects.get(furniture_name='Sofa')
# update the number of items owned
Ownership.objects.update_or_create(
owner=person, furniture=furniture, defaults=dict(items_owned=3))
# get the number of items owned
person.ownership_set.get(furniture=furniture).items_owned
# or for example
Ownership.objects.get(owner__name='Bob', furniture__name='Sofa').items_owned
You can then abstract away this complex querying and updating logic with custom managers https://docs.djangoproject.com/en/2.0/topics/db/managers/#custom-managers
I'm essentially trying to come up with my own inheritance scheme because Django's inheritance doesn't fit my needs.
I'd like parent table(class) hold common data fields.
sub classess would have its own additional data in a separate table.
class ProductBase(models.Model):
common = models.IntegerField()
def get_price(self):
return some_price
class FooProduct(ProductBase):
# no field because I'm proxy
class Meta:
proxy = True
def get_price(self):
return price_using_different_logic
class FooExtra(models.Model):
base = models.OneToOneField(ProductBase, primary_key=True)
phone = models.CharField(max_length=10)
My question is, would it be able to treat as if Foo has FooExtra's fields?
I'd like to do things like following..
foo = FooProduct.objects.create()
foo.phone = "3333" # as django does with its multiple inheritance
foo.save()
FooProduct.objects.filter(phone="3333")
I'd like to list Products of different kind(data)
I need to list them together, so abstract Base inheritance is out
from the list, I'd like to treat each model as polymorphic model, when iterating over ProductBase.objects.all(), product.get_price() will use appropriate classe's method. (without incurring join if don't have to)
When and only when I want, I retrieve the addtional table data (by something like .select_related('fooextra')
Django-polymorphic is close to what I want, but it is rather obscure what it does so I'm afraid to use it, and I think it fails #3.
If I understand well, you want inheritance and you want the fields that are specific to the child class to be on a separate table.
As far as I know, you don't need a proxy class to achieve that, you could just implement multi-table inheritance as specified in the manual at https://docs.djangoproject.com/en/1.9/topics/db/models/#multi-table-inheritance e.g.:
class Base(models.Model):
common = models.IntegerField()
class Foo(Base):
phone = models.CharField(max_length=10)
This, as explained at the link above, will automatically create a one-to-one relationship. And of course you can do foo.phone = "3333" (where foo is of type Foo) as in your example above. And the neat thing is that you can also access foo.common whereas in your example it would have been foo.base.common.
It doesn't seem like you want anything different to Django's standard inheritance.
class ProductBase(models.Model):
common1 = models.IntegerField()
common2 = models.IntegerField()
class FooProduct(ProductBase):
fooextra = models.IntegerField()
class BarProduct(ProductBase):
barextra = models.IntegerField()
If you create instances of each:
foo1 = FooProduct(common1=1, common2=1, fooextra=1)
foo2 = FooProduct(common1=1, common2=1, fooextra=2)
bar1 = BarProduct(common1=1, common2=1, barextra=1)
bar2 = BarProduct(common1=1, common2=1, barextra=2)
You can loop over all products:
for product in ProductBase.objects.all():
print product.common1, product.common2
From a ProductBase object that is actually a FooProduct, you can get the custom field with:
product.foo.fooextra
From a ProductBase object that is actually a BarProduct, you can get the custom field with:
product.bar.barextra
You can still do querying:
foo = FooProduct.objects.get(fooextra=1)
bar = BarProduct.objects.get(barextra=2)
And you can access the common fields directly on those objects:
foo.common1
bar.common2
You can use the InheritanceManager from django-model-utils if you need more control over querying etc - and this should address point 3, too: ProductBase.objects.filter(...).select_subclasses() would give you the FooProduct and BarProduct objects instead of ProductBase objects.
I'm designing a new Django app and due to several possibilities, I'm not sure which would be the best, thus I'd like opinions, and hopefully improve what I got so far.
This question comes close but not quite. This one touches the flat/nested subject which is helpful, while still not answering the question.
There are many others on the same subject, and yet none tell me what I want to know.
Background
The models have each unique properties with some shared attributes, and I need to reference them in another model, optimally with a single entry point rather than having a field for each possible model.
I want to be able to do complex Django ORM queries involving the Base class and filter by SubClass when needed. E.g Event.objects.all() to return all events. I'm aware of Django model utils Inheritance Manager and intend to use it if possible.
Also, I'll be using django admin to create and manage the objects, so an easy integration is a must. I want to be able to create a new SubEvent directly, without having first to create a Event instance.
Example
To illustrate, let's say I have the following models for app A.
class Event(models.Model):
commom_field = models.BooleanField()
class Meta:
abstract = True
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# and so on
And also an app B, where I want to be able to reference a event which can be of any type, like:
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
# This won't work, because `A.Event` is abstract.
Possible solutions
Use a GenericForeignKey.
# B.models.py
class OtherModel(models.Model):
content_type = models.ForeignKey(ContentType)
object_id = models.PositiveIntegerField()
event = GenericForeignKey('content_type', 'object_id')
What I don't like about this is that I'll lose the querying capabilities Django ORM has, and I might need to do additional fiddling to get it working on admin. Not sure, never dealt with this before
Flatten Event
I can bring it all up to the base class and have flags or checks outside the model definition, something like:
class Event(models.Model):
commom_field = models.BooleanField()
email = models.EmailField(blank=True)
title = models.TextField(blank=True)
number = models.IntegerField(default=10)
This might seem like the best idea at first, but of course there are other kind of fields, and that forces me to allow nulls/blanks for most of them (like the email field), losing the db level integrity check.
OneToOne relationships
Rather than abstract like on 1 or flatten on 2 it is possible to have a db table for each, where the models will look like:
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(models.Model):
event = models.OneToOneField(Event)
email = models.EmailField(unique=True)
class SubEventB(models.Model):
event = models.OneToOneField(Event)
title = models.TextField(blank=True)
class SubEventC(models.Model):
event = models.OneToOneField(Event)
number = models.IntegerField(default=10)
So far it solved the two initial problems, but now when I get to the admin interface, I'll have to customize each form to create the base Event before saving a SubEvent instance.
Questions
Is there a better approach?
Can any of the choices I present be improved in any direction (ORM query, DB constraints, admin interface)?
I've pondered about both answers and came up with something based off of those suggestions. Thus I'm adding this answer of my own.
I've chosen to use django-polymorphic, quite nice tool suggested by #professorDante. Since this is a multi-table inheritance, #albar's answer is also somewhat correct.
tl;dr
django-polymorphic attends the 3 main requirements:
Allow django ORM querying style
Keep db level constraints by having a multi-table inheritance and one table for each sub class
Easy django admin integration
Longer version
Django-polymorphic allows me to query all different event instances from the base class, like:
# assuming the objects where previously created
>>> Event.objects.all()
[<SubEventA object>, <SubEventB object>, <SubEventC object>]
It also has great django admin integration, allowing seamless objects creation and editing.
The models using django-polymorphic would look like:
# A.models.py
from polymorphic import PolymorphicModel
class Event(PolymorphicModel):
commom_field = models.BooleanField()
# no longer abstract
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField()
class SubEventC(Event):
number = models.IntegerField(default=10)
# B.models.py
# it doesnt have to be polymorphic to reference polymorphic models
class OtherModel(models.Model):
event = models.ForeignKey('A.Event')
Besides, I can reference only the base model from another class and I can assign any of the subclasses directly, such as:
>>> sub_event_b = SubEventB.objects.create(title='what a lovely day')
>>> other_model = OtherModel()
>>> other_model.event = sub_event_b
My .2c on this. Not sure about your design in #3. Each SubEvent subclasses Event, and has a one-to-one to Event? Isn't that the same thing?
Your proposal on the Generic Key is exactly what it is designed for.
Another possibility - Polymorphism with Mixins. Use something like Django-polymorphic, so querying returns you the subclass you want. I use this all the time and its super useful. Then make Mixins for attributes that will be reused across many classes. So a simple example, making an email Mixin
class EmailMixin(models.Model):
email = models.EmailField(unique=True)
class Meta:
abstract = True
Then use it
class MySubEvent(EmailMixin, models.Model):
<do stuff>
This way you dont have redundant attributes on subclasses, as you would if they were all in the parent.
Why not a multi-table inheritance?
class Event(models.Model):
commom_field = models.BooleanField()
class SubEventA(Event):
email = models.EmailField(unique=True)
class SubEventB(Event):
title = models.TextField(blank=True)
class SubEventC(Event):
number = models.IntegerField(default=10)