I am new in Django an have trouble figuring out the right way of making model inheritance.
Let assume that I am making some kind of food app. I would then have a model for meet, a model for fruit, a model for vegetables and so on.
My question is: How can I make a nutrition content model, which can inherit from all of the above models but only from one at a time? For instance nutrition content of apple should only inherit from the fruit model.
If there was only one food model type I would use ForeignKey to handle the inheritance. I guess this is not an option when there are several options for models to inherit from.
I would like to use on_delete=models.CASCADE for the nutrition content model. Hence the request for inheritance.
Any suggestions will be appreciated.
Python class inheritance and Django model ForeignKey relationships are two completely different things. It sounds like you're referring to the latter.
Are the fields so different between the different food types that you actually need a different model for each one? The simplest way would be to just have single Food model for all food types.
class Food(models.model):
VEGETABLE = 'vegetable'
MEAT = 'meat'
FRUIT = 'fruit'
TYPE_CHOICES = [
(VEGETABLE, 'vegetable'),
(MEAT, 'meat'),
(FRUIT, 'fruit'),
]
type = models.CharField(max_length=10, choices=TYPE_CHOICES)
nutrition_content = models.OneToOneField('NutritionContent', on_delete=models.CASCADE)
# additional fields
class NutritionContent(models.Model):
# additional fields
If your fields are so different between food types that you need to have different models for each one, you can set up Food as a parent model that all child food type models have a OneToOneField relationship with. Then the NutritionContent model can still link with Food.
class Food(models.model):
nutrition_content = models.OneToOneField('NutritionContent', on_delete=models.CASCADE)
# fields that apply to all foods
class Vegetable(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to vegetables
class Meat(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to meat
class Fruit(models.Model):
food = models.OneToOneField('Food', on_delete=models.CASCADE)
# additional fields that only apply to fruit
class NutritionContent(models.Model):
# additional fields
Related
Consider the following hierarchical data. It can be 3 to 4 levels deep. I understand I can use existing packages like django-mptt or django-treebeard to create a tree data structure.
Mercedes
Consumer
ModelA
ModelB
SUV
ModelC
Luxury
ModelD
Nissan
Consumer
ModelA
ModelB
SUV
ModelC
Now let's say I have another model called Battery. A battery can be compatible with multiple models for different market segments by different car vendors. So what I want to do is assign this battery to one or more compatible models above. I'm not sure how to accomplish this linkage in Django. Would it just be a ManytoMany field to the hierarchical model? Some pseudo-code would help.
class Battery(models.Model)
name = Charfield(max_length=50)
compatible_models = ????
I would also like to know how a query would be written. For example, I want to query all the battery that are compatible with ModelA by Mercedes, etc.
I think the below would work for you (in pure Django):
class CarBrand(models.Model):
brand_name = models.CharField(...)
class CarType(models.Model):
type_name = models.CharField(...)
available_brands = models.ManyToManyField(CarBrand)
class ModelType(models.Model):
model_name = models.CharField(...)
available_car_types = models.ManyToManyField(CarType)
class BatteryType(models.Model):
battery_name = models.CharField(...)
supported_models = models.ManyToManyField(ModelType)
You would query Batteries as follows:
BatteryType.objects.filter(
supported_models__model_name='ModelA',supported_models__available_car_types__available_brands__brand_name='Mercedes'
)
I've got these Car and Bike models I can't modify.
They may need to be linked to a Ferry model which I can modify. I want to implement ordering of these elements of two different models in the database and I want to avoid using Generic Foreign Keys.
So far this is what I've come up with:
class Car(models.Model):
pass
class Bike(models.Model):
pass
class Ferry(models.Model):
pass
class Lot(models.Model):
position = SmallInteger()
car = models.ForeignKey(to=Car, null=True, related_name="ferries")
bike = models.ForeignKey(to=Bike, null=True, related_name="ferries")
ferry = models.ForeignKey(to=Ferry, null=False, related_name="load")
Now my goal is to be able to access directly all the elements of a particular Ferry – be they cars or bikes – ordered by position, and all the ferries (in the context of ferry travel bookings there may be several) of a particular car or bike:
some_ferry.load.all().order_by("position")
some_car.ferries.all()
How do I create these relations, including a sort of combination of (Car + Bike)?
So you can put ordering on the table or as you make a query and you can use django's __ notation to use fields on related objects (more here)
For example;
class Car(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('ferries__position', )
class Bike(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('ferries__position', )
class Ferry(models.Model):
class Meta:
"""
Metadata
"""
ordering = ('load__position', )
class Lot(models.Model):
position = SmallInteger()
car = models.ForeignKey(to=Car, null=True, related_name="ferries")
bike = models.ForeignKey(to=Bike, null=True, related_name="ferries")
ferry = models.ForeignKey(to=Ferry, null=False, related_name="load")
class Meta:
"""
Metadata
"""
ordering = ('position', )
Or in your queries it might be Bike.objects.all().order_by('ferries__position')
If you define the ordering in the model meta, then that'll be the default ordering for all queries on that model so you don't then have to worry about it when you're making queries or looking at admin etc.
I don't see a way you can achieve what you want without using Generic Foreign Key, since the relationship you envision does not exist in Django (and, actually, is not a nicely normalized relational database pattern. Other database paradigms, such as OODB and GraphDB do include this pattern).
What you can do is to define a method on class Ferry that would query for all instances of Cars and Bikes and join them in a single resultset.
Another option is to use library django-model-utils, which would allow you to define a model, let's say, Transportation, that would act as some sort of virtual model, and you can define models Bike and Car as subclasses of it. With this solution, you can create a foreign key on Lot pointing to Transportation, and you can query for Bike, Car, or the generic Transportation instances.
Say if I have 3 models - bananas, oranges and apples as below.
#models.py
class banana(fruit):
pass
class apple(fruit):
pass
class orange(fruit):
pass
class fruit (models.Model):
title = models.CharField(max_length="150")
is_rotten = models.BooleanField(default=False)
class Meta:
abstract=True
In admin, I would like to display all the fruits together in a list, but with the is_rotten displayed as an editable checkbox so it's possible to view all fruits together and mass assign the is_rotten variable.
class fruitAdmin(admin.ModelAdmin):
list_display = ('title', 'is_rotten')
list_editable = ('is_rotten',)
I've got this far but seaching around multiple models in same admin list isn't bringing anything up. Is it possible to create the listing i am envisaging?
Thanks,
You need a table for fruit or another table relating all fruits with their properties (title and is_rotten). With your models as you wrote them, you can't do that, since Django admin doesn't support registering for abstract classes.
So if you change your fruit class to:
class fruit (models.Model):
title = models.CharField(max_length="150")
is_rotten = models.BooleanField(default=False)
Keep the other models as you wrote them:
class banana(fruit):
pass
class apple(fruit):
pass
class orange(fruit):
pass
And add this to your your_app/admin.py module:
class FruitAdmin(admin.ModelAdmin):
list_display = ("title", "is_rotten")
admin.site.register(myapp.models.fruit, FruitAdmin)
Usnig this code you get a list as shown in image below:
I am new to Django and databases and after reading the Django documentation on models I have the following question:
Let's say I have 3 models: VehicleName, CarManufacturer and TruckManufacturer. I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames. What is the relationship here and how to define it in Django? Is it as simple as define a models.ForeignKey(VehicleName) in both CarManufacturer and TruckManufacturer?
Thanks.
from django.db import models
class CarManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class TruckManufacturer(models.Model):
vehicle_name = models.ForeignKey(VehicleName) # IS THIS CORRECT???
# ...
pass
class VehicleName(models.Model):
# ...
To do exactly what you're describing:
I am trying to create a database relationship where CarMaunfacturer has many VehicleNames and also TruckManufacturer has many VehicleNames
You'd create a nullable foreign key on VehicleName to both of your Manufacturer models:
class CarManufacturer(models.Model):
# field definitions here
class TruckManufacturer(models.Model):
# field definitions here
class VehicleName(models.Model):
car_manufacturer = models.ForeignKey(CarManufacturer, blank=True, null=True)
truck_manufacturer = models.ForeignKey(TruckManufacturer, blank=True, null=True)
Then, instances of CarManufacturer or TruckManufacturer can get the names via the vehiclename_set attribute.
For a more advanced design, I would probably try to abstract the shared manufacturer behavior into a single model, then use multi-table inheritance:
class Manufacturer(models.Model):
# shared car and truck manufacturer fields go here
class CarManufacturer(Manufacturer):
# car manufacturer specific fields go here
class TruckManufacturer(Manufacturer):
# truck manufacturer specific fields go here
class VehicleName(models.Model):
manufacturer = models.ForeignKey(Manufacturer)
See the multi-table inheritance docs for full details.
I do not think you are understanding the manufacturer to vehicle relationship property. What I think you are trying to show is that a certain Vehicle belongs to a certain manufacturer.
This type of relationship would actually be defined in the Vehicle class, as a foreign key, called manufacturer, in the Vehicle class.
In the case you are defining many vehicles under a manufacturer, you just need to rename the property to car_model or something of the like and you should be fine.
I think you have the understanding mapped out well enough. Just remember that foreign keys are only a property of one table, and say nothing about the other table itself until the relationship is established there also.
If you're working with a larger relationship, with multiple objects, you should look into using the Many-to-many field described in the django documentation.
They have an example that shows how an Articles have many Publications:
class Publication(models.Model):
title = models.CharField(max_length=30)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.title
class Meta:
ordering = ('title',)
class Article(models.Model):
headline = models.CharField(max_length=100)
publications = models.ManyToManyField(Publication)
# On Python 3: def __str__(self):
def __unicode__(self):
return self.headline
class Meta:
ordering = ('headline',)
All,
I am missing something fundamental about the underlying model for Django's ForeingKeys vs ManyToManyFields.
Suppose I am building an application about cars. I might have the following classes:
class Car(models.Model):
carName = models.CharField()
class Manufacturer(models.Model):
manufacturerName = models.CharField()
class Wheel(models.Model):
radius = models.IntegerField()
So far so good. Now there are some relations between these classes. A car has a manufacturer and has (four) tire(s). Conceptually, there is a difference though. The manufacturer is related via "aggregation"; a manufacturer can be associated to multiple cars; deleting a Car instance should not cause that car's manufacturer to be deleted as well. The wheels are related via "composition"; every four wheels associated with a car are associated with that and only that car; delete the car and the wheels should be deleted as well.
So, intuitively, that means that I ought to do the following:
class Car(models.Model):
carName = models.CharField()
manufacturer = models.ManyToManyField("Manufacturer")
wheels = models.ForeignKey("Wheel")
Ultimately, I want to use inlineformset_factories so that users can fill in details about a car, its manufacturer and wheels all at the same time. Something like this:
class CarForm(ModelForm):
class Meta:
model = Car
class ManufacturerForm(ModelForm):
class Meta:
model = Manufacturer
class WheelForm(ModelForm):
class Meta:
model = Wheel
Manufacturer_formset = inlineformset_factory(Car,Manufacturer,formset=ManufacturerForm)
Wheel_formset = inlineformset_factory(Car,Wheel,formset=WheelForm)
But most of the documentation that I find suggests that the ForiegnKey should go from Wheel to Car. This seems backwards to me, since the Wheel_formset would then present the user with all of the fields for a Car ("carName") and not a Wheel ("radius").
Just the act of typing this question is making me confused. Can anybody shed some light on how I can build a form that has all of a car fields, and then all of a manufacturer fields, and then all of a wheel fields.
Thanks
If each car has one manufacturer, then you should use a foreign key from Car to Manufacturer. This will allow multiple cars to have the same manufacturer, and manufacturers will not be deleted when cars are deleted. A many to many field suggests that one car can have multiple manufacturers.
Wheel should have a foreign key to Car. This will allow multiple wheels to have the same car, and the default Django behaviour when a car is deleted will be to delete the wheels.
So your models should look something like this:
class Manufacturer(models.Model):
name = models.CharField()
class Car(models.Model):
name = models.CharField()
manufacturer = models.ForeignKey("Manufacturer")
class Wheel(models.Model):
radius = models.IntegerField()
car = models.ForeignKey("Car")
For your view, I would first try to write views for the forms and formsets individually, and make sure you understand the relationships between your models before you bring them all together in one view.
This Stack Overflow question explains how to use a form and inline formset together at the same time (equivalent to the Car and Wheel models in your case). For the manufacturer, you probably want to exclude the manufacturer field from your CarForm, then set it in your view before you save.
...
manufacturer = ManufacturerForm.save()
car = CarForm.save(commit=False)
car.manufacturer = manufacturer
car.save()
...