Model inheritance: benefits of explicit OneToOneField vs implicit multi-table inheritance OneToOneField - python

I read here that multi-table inheritance can cause performance issues, and instead explicit OneToOneField is recommended.
Here is my situation:
class Product(models.Model):
title = models.CharField(max_length=200)
description = models.TextField()
price = models.DecimalField(decimal_places=2, max_digits=10)
category = models.ForeignKey('Category', on_delete=models.CASCADE, blank=False)
class Book(Product):
publisher = models.CharField(max_length=50)
author = models.CharField(max_length=50)
isbn = models.CharField(max_length=50)
class Shoes(Product):
size = models.PositiveSmallIntegerField()
colour = models.CharField(max_length=20, choices=[('black', 'Black'), ('white', 'White')])
I don't understand why would explicit OneToOneField bring performance gains, when multi-table inheritance is implemented in exactly the same way:
place_ptr = models.OneToOneField(
Place, on_delete=models.CASCADE,
parent_link=True,
)
If this is true despite everything, I would like to know how could I alter my models so they use explicit OneToOneField. So, if I create a Book instance, Product.objects.all() should retrieve that instance of Book, too.

I'd be a bit skeptical of the conclusion that multi-table inheritance causes performance issues for a variety reasons:
That question is 5 years old, so the answers might be outdated.
this answer contradicts that conclusion
The only explanations given for why model inheritance is bad are that it requires LEFT JOINs or
The reason using multi-table inheritance is inefficient is because it generates an extra table for the base model, and therefore extra JOINs to access those fields.
The above statement may be true, but it doesn't explain how explicit OneToOneFields are better, which is the main premise of user193130's answer.
The real question you should ask yourself is, should I join table x and table y?, which, as turns out, is an extremely complicated question.
Here is a list of resources related to this topic, most of which support my skepticism:
a comment on dani herrera's answer:
"Q: Why are explicit OneToOneFields better? A: You can access directly to related model without left joins." this is somewhat misleading. I think you need joins anyway. The difference is whether you do it explicitly or implicitly. Please correct me if I'm wrong
to which dani herrara replies:
#eugene, you are right. Be free to improve answer. Thanks!
The last lines of Chris Pratt's answer:
Using separate models (and tables) only makes sense for the purposes of modularization -- such that you subclass Project to create a specific kind of project that requires additional fields but still needs all the fields of a generic Project.
Comment in Chris Pratt's other answer:
It depends on the situation. If there's a clear is-a relationship going on. Like class Dog(Animal), then you should use MTI (If it's called for. Some other form of inheritance may be more appropriate). But, in the case of something like auth.User, a UserProfile is not really a type of User, so OneToOneField makes more sense. However, the BIG caveat there is that UserProfile is used because User cannot be altered. Otherwise, simply adding additional fields to User would be more appropriate

Related

ForeignKey between Django Models abstract base classes

I have a Django app where I want to use a set of abstract base classes to define certain models. I want to connect some of these abstract models through foreign keys.
I know that Django does not allow models.ForeignKey to abstract models. I've done some searching on StackOverflow and the general consensus solution seems to be - using GenericForeignKey. However, from my understanding, this results in an extra db-level SELECT call. I want to avoid this because quite a few of my abstract models have this kind of relationship. An example is given below:
class Person (models.Model):
name = models.CharField (max_length=256)
class Meta:
abstract = True
class Phone (models.Model):
phone_no = models.BigIntegerField ()
owner = models.ForeignKey (Person) # This is, of course, wrong. I'd like something like this.
class Meta:
abstract = True
Besides the posts about GenericForeignKey, I also came across a post about dynamic model generation. The link is given below. The question-poster themselves have provided the answer.
Defining an Abstract model with a ForeignKey to another Abstract model
I would like to ask:
if this still holds for the current versions of Django,
if there are any caveats I should be aware of, and
if there is perhaps a more contemporary solution?
Thank you.
I have solved the issue. As pointed by Willem and Mohit, I was thinking about the problem wrongly (I come from the Cpp world and am very new to the Python/Django programming mindset).
I ended up having the abstract classes defined without any relationships and then having concrete classes derived from these abstract ones actually define the relationships. This is also in keeping with the DRY principle.

Creating a model related to an arbitrary number of other models

I deleted my previous question, since it was terribly worded and my non-working examples were just confusing.
I have a series of models, such as Vehicle, Computer, Chair, and whatnot. My goal is to be able to attach an arbitrary number of images to each of them. That's my question: what's the best design pattern to achieve this?
What I've tried so far is to create an abstract model, AttachedImage. I then inherit from this model, and create more specific models, like VehicleImage, ComputerImage, or ChairImage. But this doesn't feel like the right way to pursue this.
As I mentioned in the comment on the deleted question, the correct way to do this would be to use generic relations.
Make your AttachedImage model concrete, but add content_type and object_id fields and the content_object GenericForeignKey. That content_object field can now point to an instance of any model.
To make the reverse relationships easier, you can add GenericRelation accessors to your Vehicle, Computer and Chair models.
Thanks to Daniel Roseman's, I found out about generic relationships. You can find a great overview and tutorial about them here. There are basically two ways to achieve this.
The first one is using generic relationships as explained in the tutorial I linked. This system is very versatile and powerful, but in my case, this would add an additional layer of complexity that quite frankly I don't need.
If you're a newbie with Django like I am, you can consider a much more straightforward, but less 'systematic' pattern, also detailed in that tutorial I linked: simply add a ForeignKey field for each one of the objects you want your main model to be related to. Following the example I used in my question:
class AttachedImage(models.Model):
uuid = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False)
author = models.ForeignKey(User, on_delete=models.CASCADE)
image = models.ImageField(upload_to=image_path) # TO FIX: aƱadir la ruta
is_default = models.BooleanField(default=False)
To this, you just add fields for the relationships you will need, such as:
parent_vehicle = models.ForeignKey(Vehicle, on_delete=models.CASCADE, null=True)
parent_computer = models.ForeignKey(Computer, on_delete=models.CASCADE, null=True)
parent_chair = models.ForeignKey(Chair, on_delete=models.CASCADE, null=True)
And just use them as you regularly would. The downside is ending up with all those extra fields you don't really need, but being NULLable, they won't take up much space. The other downside is that doing this with a big number of models is probably overkill, in which case you should really use the first solution.
If you don't want to use the GenericForeignKey, you can implement your own polymorphic models, with multiple table inheritance. Following are the pseudo-models for the same:
class Attachable(models.Model):
pass
class Image(models.Model):
attachable = models.ForeignKey(Attachable, related_name='images')
class Video(models.Model):
attachable = models.ForeignKey(Attachable, related_name='videos')
class Vehicle(Attachable):
vehicle attrs...
class Computer(Attachable):
computer attrs...
class Chair(Attachable):
chair attrs...
For later optimizations, Attachable can also have an attribute which describes its subtype.

How to create a many to one relationship in Django?

as the question states, how do I do create a many-to-one relationship in django models?
Basically, I have two models: Article and Comic, I want to have one Comment model which will have a relationship with both Article and Comic, but not both.
So if a Comment object has a relationship with an Article object, then it wont have a relationship with a Comic object.
I am currently doing it the following way, which does not work:
class Article(models.Model):
#other fields
class Comic(models.Model):
#other fields
class Comment(models.Model):
article = models.ForeignKey(Article)
comic = models.ForeignKey(Comic)
I would really appreciate some help.
This is tricky. I think there are a couple ways you could model this.
Using your current way you could enforce your uniqueness constraint in the application.
class Comment(models.Model):
article = models.ForeignKey(Article)
comic = models.ForeignKey(Comic)
def save(self, *args, **kwargs):
# assert that there is either comic OR article but not both
super(Comment, self).save(*args, **kwargs)
with this way, what happens if you add another model that you want Comment to reference?? You will have to manually add the conditional for the new type in your save method and perform a migration.
Django provides GenericForeignKey field that would allow you to reference any model from Comment. https://docs.djangoproject.com/en/1.8/ref/contrib/contenttypes/#generic-relations
This would allow you to create a generic reference from Comment to either Article or Comic, and since it is only one field, would by default be mutually exclusive. I find querying and using GenericeForeignKeys awkward; but they are still an option, that might work fine for your use case.
Another powerful option, (my favorite) could be to create a polymorphic model, which would also be mutually exclusive.
Each Comment could reference a generic Piece of Content, using model inheritance. (I did not test the following, so it will probably not work as copied/pasted)
class Content(models.Model):
objects = InheritanceManager()
# shared content fields could be stored in this model
class Article(Content):
# article specific fields
class Comic(Content):
# comic specific fields
class Comment(models.Model):
content = models.OneToOneField(Content)
This is a powerful way to model the relationship of Comment to any Content. This DOES add additional query overhead, and should warrant an audit for your use case.
InheritanceManager is a utility provided by django-model-utils package, and is pretty lightweight. I have used in in production environment and it is performant, as long as you understand the additional query overheard involved with modeling your data using it. https://django-model-utils.readthedocs.org/en/latest/managers.html#inheritancemanager
The query overhead is explained in the documentation.
If you think you will add additional Content subclasses in the future this could be a scalable way to model your relationship, and provides more flexibility in filtering then GenericForeignKeys.
Well, you can add another field to you Comment model. Like
class Comment(models.Model):
article = models.ForeignKey(Article, null = True)
comic = models.ForeignKey(Comic, null = True)
assigned = models.BooleanField(initial=False)
Once a comment object is created, put either article or comic to point at another object and make assigned = True.

Model inheritance approach with Django's ORM

I want to store events in a web application I am fooling around with and I feel quite unsure about the pros and cons of each respective approach - using inheritance extensively or in a more modest manner.
Example:
class Event(models.Model):
moment = models.DateTimeField()
class UserEvent(Event):
user = models.ForeignKey(User)
class Meta:
abstract = True
class UserRegistrationEvent(UserEvent):
pass # Nothing to add really, the name of the class indicates it's type
class UserCancellationEvent(UserEvent):
reason = models.CharField()
It feels like I'm creating database tables like crazy. It would require alot of joins to select things out and might complicate querying. But it's design feels nice, I think.
Would it be more reasonable to use a "flatter" model that just has more fields?
class Event(models.Model):
moment = models.DateTimeField()
user = models.ForeignKey(User, blank=True, null=True)
type = models.CharField() # 'Registration', 'Cancellation' ...
reason = models.CharField(blank=True, null=True)
Thanks for your comments on this, anyone.
Philip
Flat is better than nested. I don't see that the "deep inheritance" is really buying you anything in this case: I'd go for the flatter model as a simpler, plainer design, with likely better performance characteristics and ease of access.
You might want to try Abstract base models. This implements inheritance without joins, by only creating tables for the derived classes, containing fields from both the parent and the derived model. This would allow you to keep your nested design, without the performance penalty of all those joins.

How to model one way one-to-one relationship in Django

I want to model an article with revisions in Django:
I have following in my article's models.py:
class Article(models.Model):
title = models.CharField(blank=False, max_length=80)
slug = models.SlugField(max_length=80)
def __unicode__(self):
return self.title
class ArticleRevision(models.Model):
article = models.ForeignKey(Article)
revision_nr = models.PositiveSmallIntegerField(blank=True, null=True)
body = models.TextField(blank=False)
On the artlcle model I want to have 2 direct references to a revision - one would point to a published revision and another to a revision that is being actively edited. However from what I understand, OneToOne and ForeignKey references generate a backreference on the other side of the model reference, so my question is, how do i create a one-way one-to-one reference in Django?
Is there some special incantation for that or do I have to fake it by including state into revision and custom implementations of the fields that ask for a revision in specific state?
Edit: I guess, I've done somewhat poor job of explaining my intent. Let's try it on a higher abstraction level:
My original intent was to implement a sort of revisioned article model, where each article may have multiple revisions, where one of those revisions may be "published" and one actively edited.
This means that the article will have one-to-many relationship to revisions (represented by ForeignKey(Article) reference in ArticleRevision class) and two one way references from Article to revision: published_revision and edited_revision.
My question is mainly, how can I model this with Django's ORM.
The back-references that Django produces are programatic, and do not affect the underlying Database schema. In other words, if you have a one-to-one or foreign key field on your Article pointing to your Revision, a column will be added to the Article table in the database, but not to the Revision table.
Thus, removing the reverse relationship from the revision to the article is unnecessary. If you really feel strongly about it, and want to document in your code that the backlink is never used, a fairly common Django idiom is to give the fields a related_name attribute like _unused_1. So your Article model might look like the following:
class Article(models.Model):
title = models.CharField(blank=False, max_length=80)
slug = models.SlugField(max_length=80)
revision_1 = models.OneToOneField(ArticleRevision, related_name='_unused_1')
revision_2 = models.OneToOneField(ArticleRevision, related_name='_unused_2')
def __unicode__(self):
return self.title
That said, it's rare that a one-to-one relationship is actually useful in an application (unless you're optimizing for some reason) and I'd suggest carefully reviewing your DB schema to make sure this is really what you want. It may make sense to keep a single ForeignKey field on your ArticleRevision pointing back to an Article (since an ArticleRevision will, presumably, always need to be associated with an Article) and adding another column to Revision indicating whether it's published.
What is wrong with the link going both ways? I would think that the OneToOneField would be the perfect choice here. Is there a specific reason why this will be a detriment to your application? If you don't need the backreference why can't you just ignore it?

Categories