9
votes

I am having a model Player with my django app .

class Player(models.Model):
    """ player model """
    name = models.CharField(max_length=100, null=True, blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    hash = models.CharField(max_length=128, null=True, blank=True)
    bookmark_url = models.CharField(max_length=300, null=True, blank=True)

As per my requirement i need to create a new model BookmarkPlayerwhich will have all fields of Player model.

Right now i have two things into my mind to do this .

  1. I can extend Player class for BookmarkPlayer model.
    class BookmarkPlayer(Player):
        """ just a bookmark player"""
        class Meta:
            app_label = "core"
  1. I can define all fields of Player model into BookmarkPlayer model.
     class BookmarkPlayer(models.Model):
            """ bookmark player model """
            name = models.CharField(max_length=100, null=True, blank=True)
            date_created = models.DateTimeField(auto_now_add=True)
            last_updated = models.DateTimeField(auto_now=True)
            hash = models.CharField(max_length=128, null=True, blank=True)
            bookmark_url = models.CharField(max_length=300, null=True, blank=True)

I just want to know which way is better to do this .Please share with my if there is another good way.

Updated Question

Knbb's idea to create a base class is interesting but i am facing issue with one of my model which is already existed into database.

My actual models :

class Address(models.Model):
    address = models.TextField(null=True, blank=True)


class Site(models.Model):
    domain = models.CharField(max_length=200)


class Player(models.Model):
    # ... other fields
    shipping_address = models.ForeignKey(Address, related_name='shipping')
    billing_address = models.ForeignKey(Address, related_name='billing')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)
    site = models.ManyToManyField(Site, null=True, blank=True)

    class Meta:
       abstract = True

Models after changes :

class Address(models.Model):
    address = models.TextField(null=True, blank=True)


class Site(models.Model):
    domain = models.CharField(max_length=200)


class BasePlayer(models.Model):
    # .. other fields
    shipping_address = models.ForeignKey(Address, related_name='shipping')
    billing_address = models.ForeignKey(Address, related_name='billing')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)
    site = models.ManyToManyField(Site, null=True, blank=True)

    class Meta:
       abstract = True

class Player(BasePlayer):
   class Meta:
       app_label = 'core'


class BookmarkPlayer(BasePlayer):
    class Meta:
        app_label = 'core'

After these changes when i am running my django server i am getting errors given below.

django.core.management.base.CommandError: One or more models did not validate:
core.test1: Accessor for field 'shipping_address' clashes with related field 'Address.shipping'. Add a related_name argument to the definition for 'shipping_address'.
core.test1: Reverse query name for field 'shipping_address' clashes with related field 'Address.shipping'. Add a related_name argument to the definition for 'shipping_address'.
core.test1: Accessor for field 'billing_address' clashes with related field 'Address.billing'. Add a related_name argument to the definition for 'billing_address'.
core.test1: Reverse query name for field 'billing_address' clashes with related field 'Address.billing'. Add a related_name argument to the definition for 'billing_address'.
core.test2: Accessor for field 'shipping_address' clashes with related field 'Address.shipping'. Add a related_name argument to the definition for 'shipping_address'.
core.test2: Reverse query name for field 'shipping_address' clashes with related field 'Address.shipping'. Add a related_name argument to the definition for 'shipping_address'.
core.test2: Accessor for field 'billing_address' clashes with related field 'Address.billing'. Add a related_name argument to the definition for 'billing_address'.
core.test2: Reverse query name for field 'billing_address' clashes with related field 'Address.billing'. Add a related_name argument to the definition for 'billing_address'

Answer :
Finally i got answer if we are using the related_name attribute on a ForeignKey or ManyToManyField into a Abstract model.
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 contain '%(app_label)s' and '%(class)s'.
https://docs.djangoproject.com/en/dev/topics/db/models/#abstract-base-classes
Now my BasePlayer model is

class BasePlayer(models.Model):
    # .. other fields
    shipping_address = models.ForeignKey(Address, related_name='%(app_label)s_%(class)s_shipping')
    billing_address = models.ForeignKey(Address, related_name='%(app_label)s_%(class)s_billing')
    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now_add=True)
    site = models.ManyToManyField(Site, null=True, blank=True)

    class Meta:
       abstract = True
2
There are several other, better ways to do this, but it depends on your exact requirements which one is the best. What's the reason you want two models in the first place?knbk
I want to bookmark Player details so i can share the session between users at my website.Prashant Gaur
So if I understand correctly you basically want to copy the details of a Player at a particular moment and save it as a BookmarkPlayer? In that case, see my answer, that would be the best way. It's also easy to add some convenience methods to convert a Player to a BookmarkPlayer and the other way around.knbk

2 Answers

13
votes

If your BookmarkPlayer needs the same data but in a different table, an abstract base model is the best way to go:

class BasePlayer(models.Model):
    name = models.CharField(max_length=100, null=True, blank=True)
    date_created = models.DateTimeField(auto_now_add=True)
    last_updated = models.DateTimeField(auto_now=True)
    hash = models.CharField(max_length=128, null=True, blank=True)
    bookmark_url = models.CharField(max_length=300, null=True, blank=True)

    class Meta:
        abstract = True

class Player(BasePlayer):
    """ player model """
    pass

class BookmarkPlayer(BasePlayer):
    """ bookmark player model """
    pass

This way, both Player and BookmarkPlayer inherit their fields from the BasePlayer model, but because BasePlayer is abstract, the models are completely decoupled.

Multi-table inheritance on the other hand would still save the fields in a single table, but add an extra table for the BookmarkPlayer with an implicit OneToOneField to the Player table.

0
votes

There are some good info about model inheritance here: https://docs.djangoproject.com/en/1.7/topics/db/models/#model-inheritance

Option 2 will introduce duplication, which is always bad. If BookmarkPlayer will not have any new fields, only different methods, I would suggest you use the "proxy model" option described in the link, since you don't need BookmarkPlayer to have it's own table in the database.

If it needs it's own table, "Multi-table inhertiance" is the way to go, which would be option 1 in your question