How to create abstract model in Django? - django

I am new to Django framework and in my project I have a model called Layer.
class Layer(models.Model):
name = models.CharField(max_length=255)
I would like layer to have a relationship with an abstract model called "Geometry", more specifically, one layer should have one or no Geometry and one Geometry should be owned by one layer.
The problem is that I have for types of Geometries and they all have different properties, so I decided to create multiple geometries:
class Circle(models.Model):
radius = models.CharField(max_length=255)
class Rectangle(models.Model):
height = models.CharField(max_length=255)
width = models.CharField(max_length=255)
I would like to have a data structure where both models are of the same type (Geometry). I would like to call layer.geometry and be able to get either a circle or a rectangle, or a cross and so on. Is that possible? And how is the database shape going to be like? Is Django going to create two different tables or one table with merged properties?
Thanks in advance

Django supports inheritance, although it is not very common. You can thus create a model Geometry, and let Circle and Rectangle inherit from that:
class Geometry(models.Model):
pass
class Circle(Geometry):
radius = models.CharField(max_length=255)
class Rectangle(Geometry):
height = models.CharField(max_length=255)
width = models.CharField(max_length=255)
We can furthermore make a ForeignKey (or another relation) to this Geometry class with:
class Layer(models.Model):
name = models.CharField(max_length=255)
geometry = models.ForeignKey(Geometry, on_delete=models.CASCADE)
Django will make migrations that, for an SQL database create tables for Geometry, Circle and Rectangle. The Circle and Rectangle models will have an implicit OneToOneRelation to the table for the Geometry named geometry_ptr_id, that thus refers to the parent.
For more information, see the section on Multi-table inheritance in the documentation.

Although you can link Cirle and Rectangle models with ForeignKey or OneToOneField, your have an option to create an AbstractModel for Geometry model.
class Geometry(models.Model):
id = models.IntegerField()
class Meta:
abstract = True
class Circle(Geometry):
radius = models.CharField(max_length=255)
class Rectangle(Geometry):
height = models.CharField(max_length=255)
width = models.CharField(max_length=255)
As such, both Circle and Rectangle models will have id field from Geometry model, but will only have 2 tables in your database. However, you wont be able to query in Geometry model.
If you need to query explicitly for Geometry model, go for ForeignKey or OneToOneField option. If not, abstract model is a cleaner approach in my opinion.
From Django Docs: Often, you will just want to use the parent class to hold information that you don’t want to have to type out for each child model. This class isn’t going to ever be used in isolation, so Abstract base classes are what you’re after.

Related

How to implement element ordering accross two different models in Django?

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.

How to nest abstract relationship in Django rest framework?

I have a model called Layer that has a one-to-one relationship with Geometry, and Geometry have the following design:
class Geometry(models.Model):
pass
class Circle(Geometry):
radius = models.CharField(max_length=255)
class Rectangle(Geometry):
height = models.CharField(max_length=255)
width = models.CharField(max_length=255)
How should I define the Layer serializer to make the layer json contain a property called "geometry" regardless of which child model it has a relationship with? I don't want the property to be called neither circle nor rectangle.
Thanks in advance!
You can use seperated serializers for each child models.You can decide with type param which serializer used for geometry key in dict.
data = {'geometry': CircleSerializer(circle_model_object).data if type == 'circle' else RectangleSerializer(rectangle_model_object).data}

Django model inherit from one of several models

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

Django 1.8 - Intermediary Many-to-Many-Through Relationship - What is the consequence of where 'ManytoManyField' is used?

An example Many-to-Many through relationship in Django:
class First(models.Model):
seconds = models.ManyToManyField(Second, through='Middle')
class Middle(models.Model):
first = models.ForeignKey(First)
second = models.ForeignKey(Second)
class Second(models.Model):
Following the documentation on intermediary models, only one model of the pair to be related contains the ManytoManyField, model First in the example above. Is this correct?
If so, which model should contain the ManytoManyField field? Are there any differences in using the relationship from either end depending on where the ManytoManyField is?
Thanks
EDIT (I should have been clearer):
I'm interested in an Intermediary table because I will have additional data to store on the relationship.
When I say usage, I don't mean defining the models, I mean using the relationship (otherwise I'd let Django do it's thing).
If I want all Seconds related to a First, would it be exactly the same as getting all Firsts related to a Second, or would the ManytoManyField make one direction easier to do than the other by introducing any extra functionality?
There shouldn't be a difference from an operational perspective, so the only difference would be in the definition of the model and things that affect it (for instance, Manager classes).
You also don't always need to define a "through" class. Django does that automatically for you, and all that class really does is maintain a third table to track the respective IDs for each related record in the two other tables. You have to decide whether you want to add anything to that third table that is important.
For instance, say you are designing a web app for a conference. They might want to store information about the attendees (both individuals and companies), as well as the speakers and sponsors (also individuals and companies). Part of your models for companies might look like this:
class Company(models.Model):
name = models.CharField(max_length=100)
sponsored_segment = models.ForeignKey(ConferenceSegment, null=True)
class ConferenceSegment(models.Model):
title = models.CharField(max_length=100)
But that gets cumbersome quickly, and you'll have lots of attending companies that have nothing to do with sponsoring. Also, you might want to track their rank/package on the website (after all, bigger sponsors get bigger placement):
class Company(models.Model):
name = models.CharField(max_length=100)
class ConferenceSegment(models.Model):
title = models.CharField(max_length=100)
sponsors = models.ManyToManyField(Company, through=u'Sponsor', related_name=u'sponsored_segments')
class Sponsor(models.Model):
company = models.ForeignKey(Company)
segment = models.ForeignKey(ConferenceSegment)
rank = models.PositiveIntegerField()
Notice also the "related_name" attribute in the ManyToManyField. This means that we can access the ConferenceSegment object via a Company instance by using that name:
c = Company.objects.get(...)
segments = c.sponsored_segments.all()
Hope this helps.
When you add a many to many field to a model a separate table is created in the database that stores the links between two models. If you don't need to store any extra information in this third table then you don't have to define a model for it.
class First(models.Model):
seconds = models.ManyToManyField(Second, related_name='firsts')
class Second(models.Model):
pass
I can't think of any difference between defining the many to many field in the First or Second models:
class First(models.Model):
pass
class Second(models.Model):
firsts = models.ManyToManyField(First, related_name='seconds')
In both cases usage is the same:
firsts = my_second.firsts
seconds = my_first.seconds

confused about django foreignkey, manytomanyfield, inlineformset_factories

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()
...