Poza składnią, jaka jest różnica między używaniem abstrakcyjnego modelu django a używaniem zwykłego dziedziczenia w Pythonie z modelami django? Plusy i minusy?
AKTUALIZACJA: Myślę, że moje pytanie zostało źle zrozumiane i otrzymałem odpowiedzi dotyczące różnicy między modelem abstrakcyjnym a klasą, która dziedziczy z django.db.models.Model. Właściwie chcę poznać różnicę między klasą modelu, która dziedziczy z klasy abstrakcyjnej django (Meta: abstract = True), a zwykłą klasą Pythona, która dziedziczy powiedzmy po „obiekcie” (a nie modelach. Model).
Oto przykład:
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(...
Odpowiedzi:
Django wygeneruje tabele tylko dla podklas
models.Model
, więc poprzednie ...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)
... spowoduje wygenerowanie pojedynczej tabeli, wzdłuż linii ...
CREATE TABLE myapp_employee ( id INT NOT NULL AUTO_INCREMENT, first_name VARCHAR(255) NOT NULL, title VARCHAR(255) NOT NULL, PRIMARY KEY (id) );
... podczas gdy ten ostatni ...
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)
... nie spowoduje wygenerowania żadnych tabel.
Możesz użyć wielokrotnego dziedziczenia, aby zrobić coś takiego ...
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)
... co spowoduje utworzenie tabeli, ale zignoruje pola zdefiniowane w
User
klasie, więc otrzymasz taką tabelę ...CREATE TABLE myapp_employee ( id INT NOT NULL AUTO_INCREMENT, title VARCHAR(255) NOT NULL, PRIMARY KEY (id) );
źródło
models.Model
, więc pola zdefiniowane w nadklasie nie zostaną pobrane (chyba że są również podklasamimodels.Model
).ModelBase
„s__new__
zaproszenia, wykonuje wstępną kontrolę klasy” atrybutów, sprawdzając, czy wartość atrybutu macontribute_to_class
atrybut - wFields
które można zdefiniować w Django wszystko zrobić, więc są zawarte w specjalnym słowniku o nazwie,contributable_attrs
który jest ostatecznie przekazywany podczas migracji w celu wygenerowania DDL.Model abstrakcyjny tworzy tabelę z całym zestawem kolumn dla każdego dziecka podrzędnego, podczas gdy użycie „zwykłego” dziedziczenia w Pythonie tworzy zestaw tabel połączonych (inaczej „dziedziczenie wielu tabel”). Rozważ przypadek, w którym masz dwa modele:
class Vehicle(models.Model): num_wheels = models.PositiveIntegerField() class Car(Vehicle): make = models.CharField(…) year = models.PositiveIntegerField()
Jeśli
Vehicle
jest to model abstrakcyjny, będziesz mieć jedną tabelę:Jeśli jednak użyjesz zwykłego dziedziczenia w Pythonie, będziesz mieć dwie tabele:
Gdzie
vehicle_id
jest link do wierszaapp_vehicle
, który zawierałby również liczbę kół samochodu.Teraz Django ładnie to połączy w formie obiektowej, abyś mógł uzyskać dostęp
num_wheels
jako atrybutCar
, ale podstawowa reprezentacja w bazie danych będzie inna.Aktualizacja
Aby odpowiedzieć na zaktualizowane pytanie, różnica między dziedziczeniem z klasy abstrakcyjnej Django a dziedziczeniem z klasy Pythona
object
polega na tym, że ta pierwsza jest traktowana jako obiekt bazy danych (więc tabele dla niej są synchronizowane z bazą danych) i zachowuje się jak plikModel
. Dziedziczenie ze zwykłego Pythona nieobject
nadaje klasie (i jej podklasom) żadnej z tych cech.źródło
models.OneToOneField(Vehicle)
byłoby równoznaczne z dziedziczeniem klasy modelu, prawda? A to spowodowałoby powstanie dwóch oddzielnych tabel, prawda?num_wheels
Atrybut na acar
, podczas gdy w przypadku aOneToOneField
będziesz musiał zrobić to samodzielnie.app_car
tabeli, tak aby zawierała równieżnum_wheels
pole zamiastvehicle_id
wskaźnika?Główna różnica polega na sposobie tworzenia tabel baz danych dla modeli. Jeśli użyjesz dziedziczenia bez
abstract = True
Django, utworzy oddzielną tabelę zarówno dla modelu nadrzędnego, jak i podrzędnego, która będzie przechowywać zmienne zdefiniowane w każdym modelu.Jeśli użyjesz
abstract = True
dla klasy bazowej, Django utworzy tabelę tylko dla klas, które dziedziczą po klasie bazowej - bez względu na to, czy pola są zdefiniowane w klasie bazowej, czy w klasie dziedziczącej.Zalety i wady zależą od architektury aplikacji. Biorąc pod uwagę następujące przykładowe modele:
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(...)
Jeśli
Publishable
klasa nie jest abstrakcyjna Django utworzyć tabelę dla publishables z kolumnamititle
idate
i oddzielnych tabelachBlogEntry
iImage
. Zaletą tego rozwiązania byłoby to, że można wyszukiwać we wszystkich publikowanych polach pola zdefiniowane w modelu podstawowym, niezależnie od tego, czy są to wpisy blogu czy obrazy. Ale dlatego Django będzie musiało robić łączenia, jeśli np. Robisz zapytania o obrazki ... Jeśli robiszPublishable
abstract = True
Django, nie utworzy tabeli dlaPublishable
, ale tylko dla wpisów na blogu i obrazków, zawierających wszystkie pola (także dziedziczone). Byłoby to przydatne, ponieważ żadne łączenia nie byłyby potrzebne do operacji takiej jak get.Zobacz także dokumentację Django dotyczącą dziedziczenia modeli .
źródło
Chciałem tylko dodać coś, czego nie widziałem w innych odpowiedziach.
W przeciwieństwie do klas Pythona, ukrywanie nazw pól nie jest dozwolone w przypadku dziedziczenia modelu.
Na przykład eksperymentowałem z problemami z przypadkiem użycia w następujący sposób:
Miałem model dziedziczący po auth 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 # ...
Potem miałem wstawek, które między innymi chciałem, aby przesłonić
related_name
nagroups
polu. Więc było mniej więcej tak: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.'))
Używałem tych 2 mixinów w następujący sposób:
class Member(PermissionMixin, WithManagedGroupMixin): pass
Więc tak, spodziewałem się, że to zadziała, ale tak się nie stało. Ale problem był poważniejszy, ponieważ błąd, który otrzymywałem, w ogóle nie wskazywał na modele, nie miałem pojęcia, co się dzieje.
Próbując rozwiązać ten problem, losowo zdecydowałem się zmienić mój miks i przekonwertować go na abstrakcyjny miks modelu. Błąd zmienił się na ten:
django.core.exceptions.FieldError: Local field 'groups' in class 'Member' clashes with field of similar name from base class 'PermissionMixin'
Jak widać, ten błąd wyjaśnia, co się dzieje.
To była moim zdaniem ogromna różnica :)
źródło
Główna różnica polega na dziedziczeniu klasy użytkownika. Jedna wersja będzie zachowywać się jak prosta klasa, a druga jak model Django.
Jeśli odziedziczysz podstawową wersję „obiektu”, twoja klasa pracownika będzie klasą standardową, a imię_nazwa nie stanie się częścią tabeli bazy danych. Nie możesz utworzyć formularza ani używać z nim żadnych innych funkcji Django.
Jeśli odziedziczysz modele. Wersja modelu, twoja klasa Employee będzie miała wszystkie metody modelu Django i odziedziczy pole first_name jako pole bazy danych, które może być użyte w formularzu.
Zgodnie z dokumentacją model abstrakcyjny „zapewnia sposób rozłożenia wspólnych informacji na poziomie Pythona, jednocześnie tworząc tylko jedną tabelę bazy danych na model podrzędny na poziomie bazy danych”.
źródło
W większości przypadków wolę klasę abstrakcyjną, ponieważ nie tworzy ona oddzielnej tabeli, a ORM nie musi tworzyć złączeń w bazie danych. A użycie klasy abstrakcyjnej w Django jest całkiem proste
class Vehicle(models.Model): title = models.CharField(...) Name = models.CharField(....) class Meta: abstract = True class Car(Vehicle): color = models.CharField() class Bike(Vehicle): feul_average = models.IntegerField(...)
źródło