Chcę to zrobić:
class Place(models.Model):
name = models.CharField(max_length=20)
rating = models.DecimalField()
class LongNamedRestaurant(Place): # Subclassing `Place`.
name = models.CharField(max_length=255) # Notice, I'm overriding `Place.name` to give it a longer length.
food_type = models.CharField(max_length=25)
To jest wersja, której chciałbym użyć (chociaż jestem otwarty na wszelkie sugestie): http://docs.djangoproject.com/en/dev/topics/db/models/#id7
Czy jest to obsługiwane w Django? Jeśli nie, czy istnieje sposób na osiągnięcie podobnych rezultatów?
python
django
django-inheritance
Johnny 5
źródło
źródło
Odpowiedzi:
Zaktualizowana odpowiedź: jak zauważyli ludzie w komentarzach, oryginalna odpowiedź nie odpowiadała właściwie na pytanie. Rzeczywiście, tylko
LongNamedRestaurant
model został utworzony w bazie danych,Place
nie było.Rozwiązaniem jest stworzenie abstrakcyjnego modelu reprezentującego „Miejsce”, np.
AbstractPlace
i odziedziczyć po nim:Przeczytaj również odpowiedź @Mark , który daje świetne wyjaśnienie, dlaczego nie możesz zmienić atrybutów odziedziczonych z klasy nieabstrakcyjnej .
(Zauważ, że jest to możliwe tylko od Django 1.10: przed Django 1.10 modyfikacja atrybutu dziedziczonego z klasy abstrakcyjnej nie była możliwa).
źródło
Place
było abstrakcyjne, a więc było nie tworzone w bazie danych. Ale OP chciał zarówno, jakPlace
iLongNamedRestaurant
być utworzony w bazie danych. Dlatego zaktualizowałem swoją odpowiedź, dodającAbstractPlace
model, który jest modelem „bazowym” (tj. Abstrakcyjnym) zarówno jakPlace
iLongNamedRestaurant
dziedziczonym. Teraz obaPlace
iLongNamedRestaurant
są tworzone w bazie danych, o co poprosił OP.Nie, to nie jest :
źródło
User._meta.get_field('email').required = True
może zadziałać, nie jestem pewien._meta
klasy nadrzędnej, np.MyParentClass._meta.get_field('email').blank = False
(Aby dziedziczoneemail
pole było obowiązkowe w Admin)Nie jest to możliwe, chyba że jest abstrakcyjne, a oto dlaczego:
LongNamedRestaurant
jest również aPlace
, nie tylko jako klasa, ale także w bazie danych. Tabela miejsc zawiera wpisy dla wszystkich czystychPlace
i dla wszystkichLongNamedRestaurant
.LongNamedRestaurant
po prostu tworzy dodatkową tabelę zfood_type
i odniesieniem do tabeli miejsc.Jeśli to zrobisz
Place.objects.all()
, otrzymasz również każde miejsce, które jestLongNamedRestaurant
i będzie to wystąpieniePlace
(bezfood_type
). WięcPlace.name
iLongNamedRestaurant.name
dzielić tę samą kolumnę bazy danych, a zatem musi być tego samego typu.Myślę, że ma to sens w przypadku normalnych modeli: każda restauracja jest miejscem i powinna mieć przynajmniej wszystko, co ma to miejsce. Może ta spójność jest także powodem, dla którego nie było możliwe dla modeli abstrakcyjnych przed 1.10, chociaż nie spowodowałoby tam problemów z bazą danych. Jak zauważa @lampslave, stało się to możliwe w 1.10. Osobiście zalecałbym ostrożność: jeśli Sub.x zastępuje Super.x, upewnij się, że Sub.x jest podklasą Super.x, w przeciwnym razie Sub nie może być używany zamiast Super.x.
Obejście problemu : Możesz utworzyć niestandardowy model użytkownika (
AUTH_USER_MODEL
), który wiąże się z duplikacją kodu, jeśli potrzebujesz tylko zmienić pole adresu e-mail. Alternatywnie możesz zostawić wiadomość e-mail bez zmian i upewnić się, że jest wymagana we wszystkich formach. Nie gwarantuje to integralności bazy danych, jeśli używają jej inne aplikacje, i nie działa na odwrót (jeśli chcesz, aby nazwa użytkownika nie była wymagana).źródło
Zobacz https://stackoverflow.com/a/6379556/15690 :
źródło
Wkleiłem kod do nowej aplikacji, dodałem aplikację do INSTALLED_APPS i uruchomiłem syncdb:
Wygląda na to, że Django tego nie obsługuje.
źródło
Ten superfajny fragment kodu pozwala na „przesłonięcie” pól w abstrakcyjnych klasach nadrzędnych.
Po usunięciu pól z abstrakcyjnej klasy nadrzędnej możesz dowolnie je przedefiniować.
To nie jest moja własna praca. Oryginalny kod stąd: https://gist.github.com/specialunderwear/9d917ddacf3547b646ba
źródło
Może mógłbyś poradzić sobie z contrib_to_class:
Syncdb działa dobrze. Nie próbowałem tego przykładu, w moim przypadku po prostu nadpisuję parametr ograniczenia, więc ... czekaj i zobacz!
źródło
Place._meta.get_field('name').max_length = 255
w treści klasy powinno załatwić sprawę, bez nadpisywania__init__()
. Byłoby też bardziej zwięzłe.Wiem, że to stare pytanie, ale miałem podobny problem i znalazłem obejście:
Miałem następujące zajęcia:
Ale chciałem, aby dziedziczone przez rok pole obrazu było wymagane przy jednoczesnym zachowaniu wartości zerowej pola obrazu superklasy. W końcu użyłem ModelForms do wymuszenia obrazu na etapie walidacji:
admin.py:
Wygląda na to, że ma to zastosowanie tylko w niektórych sytuacjach (na pewno tam, gdzie trzeba wymusić bardziej rygorystyczne reguły w polu podklasy).
Alternatywnie możesz użyć
clean_<fieldname>()
metody zamiastclean()
, np. Gdybytown
wymagane było wypełnienie pola :źródło
Nie można przesłonić pól modelu, ale można to łatwo osiągnąć, zastępując / określając metodę clean (). Miałem problem z polem e-mail i chciałem uczynić go wyjątkowym na poziomie Modelu i zrobiłem to w następujący sposób:
Komunikat o błędzie jest następnie przechwytywany przez pole formularza o nazwie „e-mail”
źródło
Moje rozwiązanie jest tak proste, jak następne
monkey patching
, zwróć uwagę, jak zmieniłemmax_length
atrybutname
pola wLongNamedRestaurant
modelu:źródło