web-dev-qa-db-fra.com

django modèles abstraits versus héritage classique

Outre la syntaxe, quelle est la différence entre l'utilisation d'un modèle abstrait Django et l'utilisation d'un héritage simple Python avec des modèles Django? Avantages et inconvénients?

UPDATE: Je pense que ma question a été mal comprise et j'ai reçu des réponses indiquant la différence entre un modèle abstrait et une classe héritant de Django.db.models.Model. Je souhaite réellement connaître la différence entre une classe de modèle qui hérite d'une classe abstraite Django (Meta: abstract = True) et une classe simple Python qui hérite de, par exemple, "objet" (et pas models.Model).

Voici un exemple:

class User(object):
   first_name = models.CharField(..

   def get_username(self):
       return self.username

class User(models.Model):
   first_name = models.CharField(...

   def get_username(self):
       return self.username

   class Meta:
       abstract = True

class Employee(User):
   title = models.CharField(...
73
rpq

Je souhaite en fait connaître la différence entre une classe de modèle qui hérite d'une classe abstraite Django (Meta: abstract = True)) et une classe Python qui hérite de dire, 'objet' (et pas de modèles.Modèle).

Django ne générera des tables que pour les sous-classes de models.Model, donc l'ancien ...

class User(models.Model):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

   class Meta:
       abstract = True

class Employee(User):
   title = models.CharField(max_length=255)

... entraînera la création d'une seule table, dans le sens de ...

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    first_name VARCHAR(255) NOT NULL,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);

... alors que ce dernier ...

class User(object):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

class Employee(User):
   title = models.CharField(max_length=255)

... ne provoquera aucune table à générer.

Vous pouvez utiliser l'héritage multiple pour faire quelque chose comme ça ...

class User(object):
   first_name = models.CharField(max_length=255)

   def get_username(self):
       return self.username

class Employee(User, models.Model):
   title = models.CharField(max_length=255)

... qui créerait une table, mais ignorera les champs définis dans la classe User, vous obtiendrez ainsi une table comme celle-ci ...

CREATE TABLE myapp_employee
(
    id         INT          NOT NULL AUTO_INCREMENT,
    title      VARCHAR(255) NOT NULL,
    PRIMARY KEY (id)
);
125
Aya

Un modèle abstrait crée une table avec l'intégralité du jeu de colonnes de chaque sous-enfant, alors que l'utilisation de "plain" Python inheritance crée un ensemble de tables liées (ou "héritage multi-table"). cas dans lequel vous avez deux modèles:

class Vehicle(models.Model):
  num_wheels = models.PositiveIntegerField()


class Car(Vehicle):
  make = models.CharField(…)
  year = models.PositiveIntegerField()

Si Vehicle est un modèle abstrait, vous aurez un seul tableau:

app_car:
| id | num_wheels | make | year

Cependant, si vous utilisez plain Python héritage, vous aurez deux tables:

app_vehicle:
| id | num_wheels

app_car:
| id | vehicle_id | make | model

vehicle_id est un lien vers une ligne dans app_vehicle qui aurait aussi le nombre de roues de la voiture.

Maintenant, Django le mettra bien en forme sous forme d'objet afin que vous puissiez accéder à num_wheels comme attribut sur Car, mais la représentation sous-jacente dans la base de données sera différente.


Mise à jour

Pour répondre à votre question mise à jour, la différence entre hériter d'une classe abstraite Django et de celle de object de Python est que cette dernière est traitée comme un objet de base de données (ses tables sont donc synchronisé avec la base de données) et il a le comportement d'un Model héritant d'un simple Python object ne donne à la classe (et ses sous-classes) aucun de ceux-ci qualités.

32
mipadi

Je voulais juste ajouter quelque chose que je n'ai pas vu dans d'autres réponses.

Contrairement à python, le masquage du nom de champ n'est pas autorisé avec l'héritage du modèle.

Par exemple, j'ai expérimenté des problèmes avec un cas d'utilisation comme suit:

J'ai eu un modèle héritant de l'authentification de Django PermissionMixin :

class PermissionsMixin(models.Model):
    """
    A mixin class that adds the fields and methods necessary to support
    Django's Group and Permission model using the ModelBackend.
    """
    is_superuser = models.BooleanField(_('superuser status'), default=False,
        help_text=_('Designates that this user has all permissions without '
                    'explicitly assigning them.'))
    groups = models.ManyToManyField(Group, verbose_name=_('groups'),
        blank=True, help_text=_('The groups this user belongs to. A user will '
                                'get all permissions granted to each of '
                                'his/her group.'))
    user_permissions = models.ManyToManyField(Permission,
        verbose_name=_('user permissions'), blank=True,
        help_text='Specific permissions for this user.')

    class Meta:
        abstract = True

    # ...

Ensuite, j'ai eu mon mixin qui, entre autres choses, je voulais qu'il remplace le related_name du champ groups. Donc, c'était plus ou moins comme ça:

class WithManagedGroupMixin(object):
    groups = models.ManyToManyField(Group, verbose_name=_('groups'),
        related_name="%(app_label)s_%(class)s",
        blank=True, help_text=_('The groups this user belongs to. A user will '
                            'get all permissions granted to each of '
                            'his/her group.'))

J'utilisais ces 2 mixins comme suit:

class Member(PermissionMixin, WithManagedGroupMixin):
    pass

Alors oui, je m'attendais à ce que cela fonctionne, mais cela ne s'est pas passé. Mais le problème était plus grave car l'erreur que je recevais ne visait pas du tout les modèles, je n'avais aucune idée de ce qui n'allait pas.

En essayant de résoudre ce problème, j'ai décidé au hasard de changer mon mixin et de le convertir en mixin abstrait. L'erreur a changé en ceci:

Django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'

Comme vous pouvez le constater, cette erreur explique ce qui se passe.

C'était une énorme différence, à mon avis :)

8
Adrián

La principale différence réside dans la création des tables de bases de données pour les modèles. Si vous utilisez l'héritage sans abstract = True Django créera une table distincte pour le modèle parent et pour le modèle enfant qui contient les champs définis dans chaque modèle.

Si tu utilises abstract = True pour la classe de base Django créera uniquement une table pour les classes qui héritent de la classe de base - que les champs soient définis dans la classe de base ou la classe héritante.

Les avantages et les inconvénients dépendent de l'architecture de votre application. Étant donné les exemples de modèles suivants:

class Publishable(models.Model):
    title = models.CharField(...)
    date = models.DateField(....)

    class Meta:
        # abstract = True

class BlogEntry(Publishable):
    text = models.TextField()


class Image(Publishable):
    image = models.ImageField(...)

Si la classe Publishable n'est pas abstraite Django créera une table pour publishables avec les colonnes title et date et des tables séparées pour BlogEntry et Image. L’avantage de cette solution est que vous pouvez interroger tous les publishables des champs définis dans le modèle de base, qu’il s’agisse d’entrées de blog ou d’images, mais Django devra faire des jointures si vous effectuez par exemple des requêtes sur des images ... Si vous faites Publishableabstract = True Django ne créera pas de table pour Publishable, mais uniquement pour les entrées de blog et les images, contenant tous les champs (y compris ceux hérités). Ce serait pratique, car aucune jointure serait nécessaire à une opération telle que get.

Voir aussi documentation de Django sur l'héritage des modèles .

6
Bernhard Vallant

La principale différence réside lorsque vous héritez de la classe User. Une version se comportera comme une classe simple, et l’autre se comportera comme un Django modeel.

Si vous héritez de la version "objet" de base, votre classe Employee sera simplement une classe standard et le prénom ne fera pas partie d'une table de base de données. Vous ne pouvez pas créer de formulaire ni utiliser d’autres fonctionnalités Django avec lui.

Si vous héritez de la version de models.Model, votre classe Employee utilisera toutes les méthodes d'un Django Model , et elle héritera du champ first_name en tant que champ de base de données peut être utilisé dans un formulaire.

Selon la documentation, un modèle abstrait "permet de factoriser les informations communes au niveau Python, tout en ne créant qu'une table de base de données par modèle enfant niveau de base de données. "

0
Brent Washburne