Skip to content Skip to sidebar Skip to footer

Django Sharing One Model Between Two Others

I built a system to review wines and foods. I quickly found myself repeating models and templates with tiny differences. Fundamentally it seems I want a review to relate to either

Solution 1:

I think Generic Foreign Key is the answer. Something like:

from django.dbimport models
from django.contrib.contenttypes.fieldsimportGenericForeignKeyfrom django.contrib.contenttypes.modelsimportContentTypeclassProduct(models.Model):
     ...

classFood(Product):
     ...

classWine(Product):
     ...

classReview(models.Model):
    ...

    content_type = models.ForeignKey(ContentType)
    object_id = models.PositiveIntegerField()
    content_object = GenericForeignKey('content_type', 'object_id')

This allows us to relate a review to any single record of any model in our project. The content_type field tracks which model you are trying to relate to (Food or Wine in this case). The object_id field track which record in the Wine or Food table we are trying to track. content_object is a convenience attribute that allows us direct access to the object (once the review has been saved).

When creating a new review you just assign the Wine or Food to the content_object field:

wine = Wine.objects.get(...)
review = Review(..., content_object=wine)
review.save()

Solution 2:

You could also use Multi-table inheritance instead of an AbstractClass for Item. Then you can set a direct ForeignKey to Item in Review. You can also combine this with InheritanceManager:

from model_utils.managers import InheritanceManager

classItem(models.Model):
    name =  models.CharField(max_length=30)
    desc = models.CharField(blank=True,max_length=100)    

    objects = InheritanceManager()    

classWine(Item):

    wine_type = models.ForeignKey(WineType)
    wine_year = models.IntegerField( choices=YEAR_CHOICES, default=datetime.datetime.now().year)

    source = models.ForeignKey(WineSource)

    def__str__(self): 
        return self.source.name +' '+self.name+ ' ' + str(self.wine_type)+ ' '+ str(self.wine_year)


classFood(Item):

    source = models.ForeignKey(FoodSource)

    def__str__(self): 
        return self.source.name +' - '+self.name

classReview(models.Model):
    rating = models.CharField(max_length=30)
    value = models.CharField(max_length=30)
    date = models.DateField(auto_now_add=True)
    person = models.ForeignKey(Person)

    item = models.ForeignKey(Item,null=True)

    classMeta():
        ordering = ['-date']

Then you can filter like this:

wine_reviews = Review.objects.exclude(item__wine__isnull=True)

food_reviews = Review.objects.exclude(item__food__isnull=True)

# and all item (directly sub-classed as wine or food:items = Item.objects.select_subclasses().all()

Post a Comment for "Django Sharing One Model Between Two Others"