Django modeling, common base and one-to-many relationship - django

I'd like to create two similar models AlbumA and AlbumB.
Both of them will have songs.
class AlbumBase(models.Model):
class Meta:
abstract=True
class Song(models.Model):
album = models.ForeignKey(AlbumBase) # problem here
class AlbumA(AlbumBase):
# with specific properties to A (DB fields)
pass
class AlbumB(AlbumBase):
# with specific properties to B (DB fields)
pass
I don't think Song can have foreign key to abstract base class.
How should I model these relationships in django?
EDIT
1. use ManyToManyField
Based on arie's answer, I could define the album-song relationship on the other side, just careful with the names (https://docs.djangoproject.com/en/dev/topics/db/models/#be-careful-with-related-name)
Another difference is that, my original question has 1-Many relationship, and this answer is Many-Many.(I want 1-Many, but could live with M-M..)
class AlbumBase(models.Model):
songs = models.ManyToManyField(Song, related_name="%(app_label)s_%(class)s_related")
class Meta:
abstract=True
class Song(models.Model):
pass
class AlbumA(AlbumBase):
pass
class AlbumB(AlbumBase):
pass
2. multitable-inheritance
Or I could use multitable-inheritance like below.
class Album(models.Model):
pass # not abstract
class Song(models.Model):
album = models.ForeignKey(Album)
class AlbumA(Album):
pass
class AlbumB(Album):
pass
3. Generic foreign Key (based on Mark's answer)
class AlbumBase(models.Model):
class Meta:
abstract=True
class Song(models.Model):
content_type = models.ForeignKey(ContentType, db_index=True, related_name='+')
object_id = models.PositiveIntegerField(db_index=True)
album = generic.GenericForeignKey()
class AlbumA(AlbumBase):
pass
class AlbumB(AlbumBase):
pass
Which one should I go with?
What are the questions that I should ask to pick among these solutions?

If you are deriving your models from an abstract class, its better to add information to these models directly.
you can rather try linking Song class to AlbumA and AlbumB.

Are you really talking about different classes of Albums with distinct properties and database fields?
If you are only concerned about handling songs that differ from album to album, I see no need for an abstract base class and would rather model my models like this:
class Song(models.Model):
title = models.CharField(max_length=100)
...
class Album(models.Model):
title = models.CharField(max_length=100)
songs = models.ManyToManyField(Song)
...
Alternatively, if you are sure that you don't have songs that exist in the context of multiple albums (say compilations or best of-albums) you could also do this:
class Song(models.Model):
title = models.CharField(max_length=100)
album = models.ForeignKey(Album)
...
class Album(models.Model):
title = models.CharField(max_length=100)
...
From what you show and describe don't see any need for GFKs or inheritance.
It's simply a matter of (simple) relations.

Related

How to add a field to model with a decorator?

I'd like to add foreign keys to virtually every model in my app to allow different Sites. For this I think it would be nice to use decorators, because I will be able to only add those FKs under certain conditions (e.g. if Sites is installed).
Unfortunately my attempt doesn't do anything:
def add_site_fk_field(model_to_annotate: models.Model):
"""Add FK to Site to Model"""
model_to_annotate.site = models.ForeignKey(Site, on_delete=models.CASCADE)
return model_to_annotate
#add_site_fk_field
class Test(models.Model):
...
Is it somehow possible to do this? I prefet not to use abstract classes.
As I know you cant do it with decorator. But you can do that with creating an abstract base model class.
For example:
from django.db import models
class Site(models.Model):
name = models.CharField(max_length=60)
class AddSiteFkField(models.Model):
fk_site = models.ForeignKey(Site, on_delete=models.CASCADE)
class Meta:
abstract = True
class Test(AddSiteFkField):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)

Django change IntegerField to DecimalField on inherited model

In Django I have 2 models that are inherited from an abstract base class. The base class has an IntegerField that I want to rename and change to DecimalField for some instances. There is a ForeignKey linking the 2 child models. I've read about model inheritance in docs, but there seems to be some constraints. What's the best way to change the IntegerField to DecimalField?
class Parent(models.Model):
intfield = models.IntegerField()
class Meta:
abstract=True
class Child1(Parent):
addedfield = models.CharField(max_length=20)
class Child2(Parent):
addedfield2 = models.DecimalField(max_digits=10, decimal_places=2)
linked = models.ForeignKey(Child1, on_delete=models.CASCADE)
class GrandChild1(Child1):
# change intfield to decimal
class GrandChild2(Child2):
# change intfield to decimal
# linked to GrandChild1 instead of Child1
You can use extra abstract models to alter the fields in the hierarchy:
class Parent(models.Model):
intfield = models.IntegerField()
class Meta:
abstract = True
class DescendantType1(Parent):
"""This is an abstract model that inherits from the Parent model, with the "type 1"
attributes.
"""
addedfield = models.CharField(max_length=20)
class Meta:
abstract = True
class DescendantType2(Parent):
"""This is an abstract model that inherits from the Parent model, with the "type 2"
attributes.
"""
addedfield2 = models.DecimalField(max_digits=10, decimal_places=2)
linked = models.ForeignKey(
"Child1",
on_delete=models.CASCADE,
# This is required on foreign key fields in abstract models.
# See the "Note about foreign key related names" below.
related_name="%(app_label)s_%(class)s_related",
related_query_name="%(app_label)s_%(class)ss",
)
class Meta:
abstract = True
class Child1(DescendantType1):
"""A descendant with "type 1" attributes."""
class Child2(DescendantType2):
"""A descendant with "type 2" attributes."""
class GrandChild1(DescendantType1):
intfield = models.DecimalField(...)
class GrandChild2(DescendantType2):
intfield = models.DecimalField(...)
linked = models.ForeignKey(
"GrandChild1",
on_delete=models.CASCADE,
)
Note about foreign key related names
An abstract model that has a foreign key needs to use a different related_name and related_query_name for each concrete model that inherits from it, otherwise the names for the reverse relationship would be the same for each subclass.
To handle this, django allows you to use template strings with the variables app_label and class so that the names are unique for the child model.
You can find more about this in the documentation.

How can redefine attribute in abstract model?

Hello I have two models
class A(models.Model):
slug = models.SlugField()
class Meta:
abstract = True
class B(A):
slug = models.CharField()
class Meta:
abstract = True
I get error AttributeError: Manager isn't available; B is abstract
How do can to redefine attribute in abstract class?
class A cannot be changedю
Abstract models don't have a manager on them because they don't map to a database table. They are used to define reusable mixins that you can compose into concrete models. To make B a concrete model remove the abstract flag and then it will have an objects attribute defined on its instances.
class A(models.Model):
slug = models.SlugField()
class Meta:
abstract = True
class B(A):
slug = models.CharField()
As an aside, as things stand with your models this is a pointless hierarchy because the slug field on B overrides the slug field that is being inherited from A and therefore there is currently zero shared custom functionality between the two definitions. You may as well just have B inherit from models.Model directly.

Automatically set the table name for a Django Model to the name of its class

Currently, most of my models look like this:
class A(models.Model):
# model attributes
class Meta:
db_table = 'A'
class B(models.Model):
# model attributes
class Meta:
db_table = 'B'
Is there a way to do this automatically? I tried adding the meta class after defining the class, but because of how Django handles Meta classes for models, this doesn't work. Am I just stuck defining the Meta classes by hand?
Not sure why you want to do that but you could do something like:
for model in models:
model._meta.db_table = model.__class__.__name__

Mixin inhheritance for models in django

Is there a way to build django models hierarchy like this?
class LikableObjectMixin(models.Model):
# mixin for all likable objects: posts, photos, etc
likers = models.ManyToManyField(Account)
class Meta:
abstract = True
def save():
super(LikableObjectMixin, self).save()
class Post(LikableObjectMixin, models.Model):
message = models.TextField(_('Post'))
author = models.ForeignKey(settings.AUTH_USER_MODEL, related_name='posts', blank=True, null=True)
created = models.DateTimeField(auto_now_add=True)
Can south work with this kind of inheritance? Is this an appropriate way to build model hierarchy?
Django=1.5.1
Yes, it's perfectly fine. South will create proper m2m relations for all your models that inherit from your mixin. You don't even have to write save method explicitly. So:
class LikableObjectMixin(models.Model):
likers = models.ManyToManyField(Account)
class Meta:
abstract = True
class Post(LikableObjectMixin):
message = models.TextField(_('Post'))