Comment ajouter un champ calculé à un modèle Django
j'ai un modèle simple Employee
qui inclut les champs firstname
, lastname
et middlename
.
du côté administratif et probablement ailleurs, je voudrais l'afficher comme:
lastname, firstname middlename
pour moi l'endroit logique pour faire ceci est dans le modèle en créant un champ calculé comme tel:
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)
en fin de compte, je pense que ce dont j'ai besoin est d'obtenir la valeur des champs de nom comme chaînes. L'erreur que j'ai obtiens est value_to_string() takes exactly 2 arguments (1 given)
. Valeur de chaîne veut self, obj
. Je ne sais pas ce que obj
signifie.
Il doit y avoir un moyen facile de faire cela, je suis sûr que je ne suis pas le premier à vouloir le faire.
Edit: C'est mon code modifié à la réponse de Daniel. L'erreur que j'obtiens est:
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)
5 réponses
ce n'est pas quelque chose que tu fais comme un champ. Même si cette syntaxe fonctionnait, elle ne donnerait la valeur que lorsque la classe était définie, pas au moment où vous y accédez. Vous devez faire cela comme une méthode, et vous pouvez utiliser le @property
décorateur pour le faire ressembler à un attribut normal.
@property
def name(self):
return ''.join(
[self.lastname,' ,', self.firstname, ' ', self.middlename])
self.lastname
etc apparaissent comme juste leurs valeurs, donc pas besoin d'appeler une autre méthode pour les convertir.
Ok... La réponse de Daniel Roseman semblait devoir marcher. Comme c'est toujours le cas, vous trouverez ce que vous recherchez après avoir posté la question .
du django 1.5 docs j'ai trouvé cet exemple qui a fonctionné directement de la boîte. Merci à tous pour votre aide.
voici le code qui a fonctionné:
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)
Daniel Roseman, la solution d'un champ calculé un attribut d'un Model
, mais il ne pas le rendre accessible par QuerySet méthodes (eg. . all()
, .values()
). C'est parce que les méthodes QuerySet appellent la base de données directement , en contournant le django Model
.
étant donné que les QuerySets accèdent directement à la base de données, la solution consiste à outrepasser la méthode Manager
's .get_queryset()
en ajoutant votre champ calculé. Le champ calculé est créé en utilisant .annotate()
. Enfin, vous avez placé le objects
Manager dans votre Model
sur votre nouveau Manager
.
voici un code qui démontre ceci:
models.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()
Maintenant, vous pourrez appeler .values()
ou .all()
et accéder à l'attribut nouvellement calculé link
comme déclaré dans le Manager
.
Il aurait également été possible d'utiliser autres fonctions dans .annotate()
, comme F()
.
je crois que l'attribut ne serait toujours pas disponible dans object._meta.get_fields()
. Je crois que vous pouvez l'ajouter ici, mais je n'ai pas exploré comment les modifications/commentaires seraient utiles.
dans ce cas si vous allez utiliser le champ pour la représentation dans le site d'administration et de telles questions, vous pourriez mieux envisager de passer outre str () ou unicode () méthode de la classe comme il est mentionné dans la documentation de django ici :
class Employee(models.Model):
# fields definitions
def __str__(self):
return self.lastname + ' ,' + self.firstname + ' ' + self.middlename
j'ai récemment travaillé sur une bibliothèque qui peut résoudre le problème assez facilement.
https://github.com/brechin/django-computed-property
installez cela, ajoutez à INSTALLED_APPS et ensuite
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')