Am trying to build a project and it seems that using model inheritance for my requirements can work the best, I have one base model in which I have several model uses it for inheritance. In the base model, I have a slug field which is unique across all (for integrity)
In my child model, sometimes I want to create a record but if a parent already exists, I want to create only the child and link to the parent directly. for example,
class Base(models.Model):
slug = models.SlugField(_('Slug'), unique=True)
#Other fiels
class ChildA(Base):
height = models.CharField(max_length=100, )
class ChildB(Base):
is_for_sale = models.BooleanField(_('Is active'), default=True, )
# when creating ChildA, it will automtically insert into base model as well
ChildA.objects.create(slug='bmw', height='11')
# now for childB, I want the newly created object to link to an existing record in Base where slug
# is the unique value, is it possible todo such a thing?
ChildB.objects.create(slug='bmw', is_for_sale=True)
I think you might have misunderstood inheritance at this point.
If I get it right, you say that slug must be unique. Therefore you restrict the amount of Classes with a certain slug to '1'
Your Classes ChildB and ChildA are not "linked" to BaseClass, but are descendants of BaseClass, therefore an instance of a child is also an instance of BaseClass.
To me it seems you a looking for a relation between a Slug Object and multiple other Objects with different properties.
Instead of trying to derive from Class Base I would suggest a relation like:
class ChildA(models.Model):
height = models.CharField(max_length=100, )
slug = ForeignKey(Base)
This will ensure that the right Slug Object will be used and not created again.
If you need to keep that line of inheritance, keep your base class, but extract the slug field into its own class like
class Slug(models.Model):
slug = models.SlugField(_('Slug'), unique=True)
class Base(models.Model):
# all your other fields
slug = ForeignKey(Slug)
class ChildA(Base):
height = models.CharField(max_length=100, )
class ChildB(Base):
is_for_sale = models.BooleanField(_('Is active'), default=True, )
Once you are here you can use the constructor of these classes to enforce further restrictions on Slug if they are not yet provided
If you just want a set of common attributes, you want to make your base model abstract, otherwise django will create a table for it.
If instead you are interested in a relation among all subclasses of Base, then you have to give up on inheritance and use a foreign key from every child to Base:
class ChildA(models.Model):
base = ForeignKey(Base)
height = models.CharField(max_length=100)
Related
I have these Django models:
class Base(models.Model):
name = models.CharField(max_length=255)
class Restaurant(Base):
pass
class Hotel(Base)
pass
class Message(models.Model)
text = models.TextField()
parent = models.ForeignKey(Base, on_delete=models.CASCADE)
I would like to set up a query to retrieve all of the messages left about Hotels only. But how to do that?
Message.objects.filter(has_attr("Hotel"))
Obviously this doesn't work but something like that is what I am looking for.
An inherited model has an implicit OneToOneField from the child to the parent. This relation in reverse has, by default, as related name the name of the class, so hotel.
We thus can check if there exists a Hotel object for the given parent with:
Message.objects.filter(parent__hotel__isnull=False)
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.
What is the optimal way to implement standard fields that I would like all classes in Django models to have?
Option 1
Option 1 is to create the standard fields in a parent class and then have every class inherit that class. For instance:
Option 2
Option 2 is to create a class with the standard fields that has a composite relationship to the other classes. For instance:
Option 3
Option 3 is simply to duplicate these fields in each of the classes that wishes to use them (which violates the DRY principle of Django). For example:
Which is optimal for a notes field and created and last modified datetimes (and in the future also user id)? I clearly will wish to use these fields for auditing purposes, which means they will be displayed in Django Admin in some way. Keep this in mind when helping me determine which approach is optimal.
Your question suggests that you want to build an abstract class to use as a base class for your models to inherit from. There is some really great documentation here.
For example:
class CommonInfo(models.Model):
notes = models.TextField(blank=True, null=True)
created_at = models.DateTimeField(blank=True, default=datetime.datetime.now)
last_modified_at = models.DateTimeField(blank=True, default=datetime.datetime.now)
class Meta:
abstract = True # This line is what makes the class an abstract class
def we_all_do_this(self):
pass
def we_all_do_this_too(self):
pass
class ChildA(CommonInfo): # Note that ChildA class inherits from the CommonInfo abstract base class
name = models.CharField(blank=True, null=True, max_length=100)
state = models.CharField(blank=True, null=True, max_length=100)
def only_i_do_this(self):
pass
class ChildB(CommonInfo): # Note that ChildB class inherits from the CommonInfo abstract base class
title = models.CharField(blank=True, null=True, max_length=100)
date = models.DateField(default=datetime.datetime.today)
def only_i_do_this(self):
pass
I don't think there's a right answer to this question but if it is compulsory fields I expect to implement across all models in a project, I would go with the parent class approach.
i.e., your option 1.
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
Given the following models:(don't mind the TextFields there're just for illustration)
class Base(models.Model):
field1 = models.TextField()
class Meta:
abstract=True
class Child1(Base):
child1_field = models.TextField()
class Child2(Base):
child2_field = models.TextField()
class Content(models.Model):
aso_items = models.ManyToManyField('Base')
According to these definitions a Content object can be associated with more than one Base object, eg. an interview(=Content object) can be linked with a musician(=Child1 object), a filmdirector(=Child2), etc.
Now, for my question:
Is it possible to filter Content objects according to which model the aso_items field points to?
An example : Say I would like a Queryset containing all the Content objects that are associated with a specific object of Child1(eg. all the interviews associated with the musician Bob Dylan), how can I achieve this?
Further, what if I'd want a QuerySet containing all the Content objects that are associated with Child1 objects?(eg. all the interviews that associated with musicians)
How does this change the filtering?
Thanks in advance
ps: I'm experiencing some problems with white space in the preview, forgive me
You should check the section of the Django docs regarding using related_name for abstract base classes. http://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name
To quote the docs:
If you are using the related_name
attribute on a ForeignKey or
ManyToManyField, you must always
specify a unique reverse name for the
field. This would normally cause a
problem in abstract base classes,
since the fields on this class are
included into each of the child
classes, with exactly the same values
for the attributes (including
related_name) each time.
To work around this problem, when you
are using related_name in an abstract
base class (only), part of the name
should be the string %(class)s. This
is replaced by the lower-cased name of
the child class that the field is used
in. Since each class has a different
name, each related name will end up
being different.
Using this information I would recommend moving the m2m field into the Base class:
class Content(models.Model):
# Add remaining fields for Content
pass
class Base(models.Model):
field1 = models.TextField()
items = models.ManyToManyField(Content,related_name="%(class)s_related")
class Meta:
abstract=True
class Child1(Base):
child1_field = models.TextField()
class Child2(Base):
child2_field = models.TextField()
Apparently a ForeignKey relation(or ManyToMany for that matter) with a abstract class isn't allowed.
I get the following error : 'AssertionError: ForeignKey cannot define a relation with abstract class Artiest'.
A possible solution is to define the base class as non-abstract, however this implies that one could instantiate models of the base class. Which isn't the behavior I want.(after all it was an abstract class)
Has someone come accross the same problem how did you solve it? Any alternatives?
Have a look at http://www.djangoproject.com/documentation/models/generic_relations/ which goes through generic relations. Your Content model would match up to their TaggedItem model, and your Base model would match up to their Animal/Vegetable/Mineral model (with Child1 and Child2 extending).
Getting all of the Content objects for a single child would be (assuming you set the GenericRelation to contents inside Base):
child_contents = childObject.contents.all()
And to get all Content objects for a model:
ctype = ContentType.objects.get_for_model(Child1)
all_child_contents = Content.objects.filter(content_type=ctype)