2013-07-16 20 views
24

Mam prosty model Employee, który zawiera pola firstname, lastname i middlename.Jak dodać pole obliczeniowe do modelu Django

Po stronie administratora i prawdopodobnie gdzie indziej, chciałbym, aby wyświetlić że jako:

lastname, firstname middlename 

Dla mnie logicznym miejscem na to jest w modelu poprzez stworzenie pola obliczeniowego jako takie:

from django.db import models 
from django.contrib import admin 

class Employee(models.Model): 
    lastname = models.CharField("Last", max_length=64) 
    firstname = models.CharField("First", max_length=64) 
    middlename = models.CharField("Middle", max_length=64) 
    clocknumber = models.CharField(max_length=16) 
    name = ''.join(
     [lastname.value_to_string(), 
     ',', 
     firstname.value_to_string(), 
     ' ', 
     middlename.value_to_string()]) 

    class Meta: 
     ordering = ['lastname','firstname', 'middlename'] 

class EmployeeAdmin(admin.ModelAdmin): 
    list_display = ('clocknumber','name') 
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}), 
     ] 

admin.site.register(Employee, EmployeeAdmin) 

Ostatecznie to, czego potrzebuję, to uzyskanie wartości pól nazw jako ciągów. Błąd, który otrzymuję, to value_to_string() takes exactly 2 arguments (1 given). Wartość do napisu ma wartość self, obj. Nie jestem pewien, co oznacza obj.

Musi być łatwy sposób na zrobienie tego, jestem pewien, że nie jestem pierwszym, który tego chce.

Edytuj: To jest mój kod zmodyfikowany do odpowiedzi Daniela. Otrzymuję błąd:

django.core.exceptions.ImproperlyConfigured: 
    EmployeeAdmin.list_display[1], 'name' is not a callable or an 
    attribute of 'EmployeeAdmin' of found in the model 'Employee'. 


from django.db import models 
from django.contrib import admin 

class Employee(models.Model): 
    lastname = models.CharField("Last", max_length=64) 
    firstname = models.CharField("First", max_length=64) 
    middlename = models.CharField("Middle", max_length=64) 
    clocknumber = models.CharField(max_length=16) 

    @property 
    def name(self): 
     return ''.join(
      [self.lastname,' ,', self.firstname, ' ', self.middlename]) 

    class Meta: 
     ordering = ['lastname','firstname', 'middlename'] 

class EmployeeAdmin(admin.ModelAdmin): 
    list_display = ('clocknumber','name') 
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}), 
] 

admin.site.register(Employee, EmployeeAdmin) 
+1

możliwe duplikat [Django Model Wartość pola obliczenie innych fileds] (http://stackoverflow.com/questions/11465293/django-model-field-value-is-calculation-of-other-fileds) – Anto

Odpowiedz

28

OK ... Odpowiedź Daniela Rosemana wydawała się, że powinna zadziałać. Jak zawsze, po wpisaniu pytania znajduje się to, co szukasz .

Od django 1.5 docs I found this example, który działał od razu po wyjęciu z pudełka. Dziękuję wszystkim za pomoc.

Oto kod, który pracował: rozwiązanie

from django.db import models 
from django.contrib import admin 

class Employee(models.Model): 
    lastname = models.CharField("Last", max_length=64) 
    firstname = models.CharField("First", max_length=64) 
    middlename = models.CharField("Middle", max_length=64) 
    clocknumber = models.CharField(max_length=16) 

    def _get_full_name(self): 
     "Returns the person's full name." 
     return '%s, %s %s' % (self.lastname, self.firstname, self.middlename) 
    full_name = property(_get_full_name) 


    class Meta: 
     ordering = ['lastname','firstname', 'middlename'] 

class EmployeeAdmin(admin.ModelAdmin): 
    list_display = ('clocknumber','full_name') 
    fieldsets = [("Name", {"fields":(("lastname", "firstname", "middlename"), "clocknumber")}), 
] 

admin.site.register(Employee, EmployeeAdmin) 
44

To nie jest coś, co robisz jako pole. Nawet gdyby ta składnia działała, podałaby wartość tylko wtedy, gdy klasa była zdefiniowana, a nie w momencie, w którym się do niej odwołuje. Powinieneś to zrobić jako metodę i możesz użyć dekoratora @property, aby wyglądał jak normalny atrybut.

@property 
def name(self): 
    return ''.join(
     [self.lastname,' ,', self.firstname, ' ', self.middlename]) 

self.lastname itp pojawiają się tylko ich wartości, więc nie ma potrzeby, aby wywołać jakąkolwiek inną metodę, aby je przekonwertować.

+0

Założono, że jest on umieszczony w modelu pracownika ... który dał mi błąd. Czy ta metoda powinna przejść do klasy EmployeeAdmin? – cstrutton

+0

Nie, w modelu. Jaki błąd? –

+0

To jest sposób na zrobienie tego. +1 – Matthias

6

Daniel Roseman sprawia pola obliczeniowego atrybutem Model, jednak does not make it accessible via QuerySet methods (np all(), .values()..). Jest tak, ponieważ metody QuerySet call the database directly, omijając django Model.

Ponieważ kwerendy mają bezpośredni dostęp do bazy danych, rozwiązaniem jest zastąpienie metody Manager za pomocą dołączonego pola obliczeniowego. Obliczone pole jest tworzone przy użyciu .annotate(). Na koniec ustawiasz objects Manager w swoim Model na swoim nowym Manager.

Oto kod demonstrując w ten sposób:

modele.py

from django.db.models.functions import Value, Concat 
from django.db import Model 

class InvoiceManager(models.Manager): 
    """QuerySet manager for Invoice class to add non-database fields. 

    A @property in the model cannot be used because QuerySets (eg. return 
    value from .all()) are directly tied to the database Fields - 
    this does not include @property attributes.""" 

    def get_queryset(self): 
     """Overrides the models.Manager method""" 
     qs = super(InvoiceManager, self).get_queryset().annotate(link=Concat(Value("<a href='#'>"), 'id', Value('</a>'))) 
     return qs 

class Invoice(models.Model): 
    # fields 

    # Overridden objects manager 
    objects = InvoiceManager() 

Teraz będzie można zadzwonić .values() lub .all() i dostępu do nowo obliczoną link atrybut deklarowanego w Manager.

Byłoby również możliwe użycie other functions w .annotate(), na przykład F().

Uważam, że atrybut nadal nie byłby dostępny w object._meta.get_fields(). Sądzę, że możesz dodać to tutaj, ale nie zbadałem, w jaki sposób - wszelkie modyfikacje/komentarze byłyby pomocne.

+0

Tylko dla własnego zrozumienia, dlaczego nie jest def __str __ (self): return self.whatever adekwatne rozwiązanie tego problemu? Jest to wspomniane w dokumentach bezpośrednio w części, do której odnosi się OP (wersja Django 1.10) https://docs.djangoproject.com/en/1.10/topics/db/models/#model-methods. Czy jest tak, że możesz mieć więcej niż jeden sposób wywoływania lub nazywania obiektu? Alternatywnie, możesz zrobić coś takiego jak def__str2__ return self.othername? –

+0

@ MalikA.Rumi 'self.whatever' będzie stosowane tylko do pojedynczej instancji modelu, ale nie będzie stosowane do QuerySets (czyli wszystkich instancji zwracanych przez' .all() 'lub' .filter() ') . 'self.whatever' odnosi się do pojedynczej instancji i nie istnieje dla QuerySets. –

+0

Dzięki Alex, działa to dla mnie z Django 1.11. Również musiałem używać tych pól z adnotacjami w Administracji, ale nie było to możliwe, ale obejście [tutaj] (https://stackoverflow.com/a/26618982/503743) pozwala na użycie tego również z interfejsami administracyjnymi. – Mrdev

1

W tym przypadku, jeśli tylko będzie korzystać z boiska do reprezentacji w miejscu admina i takich problemów, może lepiej rozważyć nadrzędnymi str() lub unicode() metoda klasy jak to wspomniano w dokumentacji django here:

class Employee(models.Model): 
    # fields definitions 
    def __str__(self): 
     return self.lastname + ' ,' + self.firstname + ' ' + self.middlename 
0

niedawno pracował w bibliotece, które mogą rozwiązać problem z którym masz dość łatwo.

https://github.com/brechin/django-computed-property

Instalacja że dodanie INSTALLED_APPS i

class Employee(models.Model): 
    ... 
    name = computed_property.ComputedCharField(max_length=3 * 64, compute_from='full_name') 

    @property 
    def full_name(self): 
     return '{LAST}, {FIRST} {MIDDLE}'.format(LAST=self.lastname, FIRST=self.firstname, MIDDLE=self.middlename')