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}
Related
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.
I have the following models based on a shared base model:
class Pet(models.Model):
created = models.DateTimeField(auto_now_add=True)
user = models.ForeignKey('auth.User', related_name='pet', on_delete=models.CASCADE)
name=models.CharField(max=100)
class Meta:
abstract = True
ordering = ['-created']
def __str__(self):
return str(self.id)
class Dog(Pet):
legs = models.BigIntegerField()
class Bird(Pet):
wings = models.BigIntegerField()
cage = models.BigIntegerField()
Now I would like to get a rest ViewSet that looks like this when I ask for a User:
{
username: "PeterSchmidt"
email:"ps#gmail.com"
pets{
{
name="shaggy"
type="dog"
legs=4
}
{
name="Jaques"
type="bird"
wings=2
cage=1
}
}
}
Basicly I want a way of having a baseclass model that has its own viewset (multiple serilizers are fine) that I can just use to get all the pets no matter what type they are.
Can this be done within standard django rest?
You can use django-polymorphic to create polymorphic models. General recommendation is that even if the database polymorphism looks clean in the code, it is not recommended thing to do and even if it is not obvious, the code get's more complex.
The way I would solve this would be having one model with diferrent nullable fields for diferrent types, the type choice field and then one wrapper serializer with keys type, data where key would be the type of the animal and in data would be serialized animal. This could be done using serialized of the Pet model with one SerializerMethodField which will serialize the instance of Pet using diferrent serializer for each type.
Django polymorphic
Django rest polymorphic
Realated question - There is nicely explained the way ti serialize more models with one serializer
I.e. we have SomeSeries with several SomeDecors, where ForeignKey of SomeDecor points to SomeSeries. I want both to be abstract and later instantiate several pairs of it (with it's own tables in db). Is it possible?
I.e.
class SomeSeries(models.Model):
class Meta:
abstract = True
vendor = models.ForeignKey(Vendor)
name = models.CharField(max_length=255, default='')
def __unicode__(self):
return "{} {}".format(self.vendor, self.name)
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = models.ForeignKey(SomeSeries) # some magic here to make ForeignKey to abstract model
texture = models.ImageField()
# -------------------------------------------
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
# Some magic to make PlinthDecor.series points to PlinthSeries
EDIT
Actually I don't want complicity of polymorphic relations, I want pure abstract models just to save typing (what abstract models are initially for). Suppose in my case the simplest way is to exclude ForeignKey from base model and type it only in all inherited models:
class SomeSeries(models.Model):
class Meta:
abstract = True
#...
class SomeDecor(WithFileFields):
class Meta:
abstract = True
series = None #?
#..
texture = models.ImageField()
def do_anything_with_series(self): pass
class PlinthSeries(SomeSeries): pass
class PlinthDecor(SomeDecor): pass
series = models.ForeignKey(PlinthSeries)
You can't create ForeignKey referencing abstract model. It's, even, doesn't make any sense, because ForeignKey translates into Foreign Key Constraint which have to reference existing table.
As a workaround, you can create GenericForeignKey field.
You can not do it because if you create two class inherit from your abstract class to what class your foreignkey should do? for first or for second?
So you need to create GenericForeignKey or not do any field and only after create model inherits from your abstract model add your foreign key.
I have the following model parent
class ModelParent(PolymorphicModel):
company = models.CharField(max_length=50)
.......
and the model child
class ModelChild(ModelParent)
company = models.CharField(max_length=10, blank=True)
...........
how can I make the model child company attribute overwrite the parent company model attribute without making the abstract parent model
This is not possible without an abstract parent model, unfortunately.
Field name “hiding” is not permitted
In normal Python class inheritance, it is permissible for a child class to override any attribute from the parent class. In Django, this isn’t usually permitted for model fields. If a non-abstract model base class has a field called author, you can’t create another model field or define an attribute called author in any class that inherits from that base class.
This restriction doesn’t apply to model fields inherited from an abstract model. Such fields may be overridden with another field or value, or be removed by setting field_name = None.
A recommendation instead would be to simply create a property or rename the child model's field. Another thing you could do is remove the parent model's "company" field and move it to all of the children models instead.
class ModelChild(ModelParent)
child_company = models.CharField(max_length=10, blank=True)
...........
I'm trying to create a Django ORM mapping that's compatible with an existing data model, so I'm trying to work with an existing set of table and column names.
I've got a multi-table inheritance situation where a class InformationObject derives from class Object. I'd like to let Django handle this the usual way:
class Object(models.Model):
class Meta:
db_table = "object"
class InformationObject(Object):
class Meta:
db_table = "information_object"
In this case Django would automatically create a one-to-one field on the inheriting model called object_ptr_id. However, on the schema I'm constrained to use, the reference to the Object is simply called "id". So:
Is there a way to somehow specify the name of the column Django auto-magically uses for multi-table inheritance?
The alternative, which I'll have to use otherwise, is to use an explicit one-to-one field, but then I won't be able to inherit non-database methods from the Object model:
class Object(models.Model):
class Meta:
db_table = "object"
class InformationObject(models.Model):
class Meta:
db_table = "information_object"
id = models.OneToOneField(Object, primary_key=True, db_column="id")
Any ideas? Maybe I could create a common base class for both of them and put non-db methods there...?
From the django docs (development version):
As mentioned, Django will automatically create a OneToOneField linking your child class back any non-abstract parent models. If you want to control the name of the attribute linking back to the parent, you can create your own OneToOneField and set parent_link=True to indicate that your field is the link back to the parent class.
As mentioned by #fusion quoting from the docs, you will have to create a OneToOneField if you want to specify the column, while using model inheritance. The inherited fields will be available in the child class in both self scope AND the one-to-one field.
class Object(models.Model):
class Meta:
db_table = "object"
column_1 = models.CharField()
class InformationObject(Object):
class Meta:
db_table = "information_object"
# arbitrary property name (parent_link)
parent_link = models.OneToOneField(Object, primary_key=True, db_column="id", parent_link=True)
In this example:
>>> inf_obj = InformationObject.objects.get(pk=1)
>>> print inf_obj.column_1 == inf_obj.parent_link.column_1
True