Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Create a new model which have all fields of currently existing model

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
like image 956
Prashant Gaur Avatar asked Dec 03 '14 12:12

Prashant Gaur


People also ask

How do I add a model field in Django?

To answer your question, with the new migration introduced in Django 1.7, in order to add a new field to a model you can simply add that field to your model and initialize migrations with ./manage.py makemigrations and then run ./manage.py migrate and the new field will be added to your DB.

What does Models model mean in Django?

A model is the single, definitive source of information about your data. It contains the essential fields and behaviors of the data you're storing. Generally, each model maps to a single database table. The basics: Each model is a Python class that subclasses django.db.models.Model .


1 Answers

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.

like image 171
knbk Avatar answered Oct 12 '22 23:10

knbk