J'ai un modèle Employee
simple qui comprend les champs firstname
, lastname
et middlename
.
Du côté administrateur et probablement ailleurs, je voudrais afficher cela comme:
lastname, firstname middlename
Pour moi, l'endroit logique pour le faire est dans le modèle en créant un champ calculé en tant que 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, ce dont je pense avoir besoin, c'est d'obtenir la valeur des champs de nom sous forme de chaînes. L'erreur que j'obtiens est value_to_string() takes exactly 2 arguments (1 given)
. La valeur de la chaîne veut self, obj
. Je ne sais pas ce que signifie obj
.
Il doit y avoir un moyen facile de le faire, je suis sûr que je ne suis pas le premier à vouloir le faire.
Edit: Ceci est mon code modifié pour 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)
D'accord ... La réponse de Daniel Roseman semblait avoir dû fonctionner. Comme c'est toujours le cas, vous trouvez ce que vous cherchez après avoir posté la question .
À partir de la Django 1.5 docs j'ai trouvé cet exemple qui a fonctionné dès la sortie de la boîte. Merci a 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)
Ce n'est pas quelque chose que vous faites en tant que champ. Même si cette syntaxe fonctionnait, elle ne donnerait la valeur que lorsque la classe a été définie, pas au moment où vous y accédez. Vous devez le faire comme 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 uniquement comme leurs valeurs, donc pas besoin d'appeler une autre méthode pour les convertir.
La solution de Daniel Roseman fait d'un champ calculé un attribut d'un Model
, mais il ne le rend pas accessible via les méthodes QuerySet (par exemple .all()
, .values()
). En effet, les méthodes QuerySet appeler directement la base de données , contournant le Django Model
.
Étant donné que les QuerySets accèdent directement à la base de données, la solution consiste à remplacer la méthode .get_queryset()
de Manager
en ajoutant votre champ calculé. Le champ calculé est créé à l'aide de .annotate()
. Enfin, vous définissez le gestionnaire objects
dans votre Model
sur votre nouveau Manager
.
Voici un code qui le démontre:
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 link
nouvellement calculé comme déclaré dans 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 pense que vous pouvez l'ajouter ici, mais je n'ai pas exploré comment - des modifications/commentaires seraient utiles.
J'ai récemment travaillé sur une bibliothèque qui peut résoudre le problème que vous rencontrez assez facilement.
https://github.com/brechin/Django-computed-property
Installez-le, ajoutez-le à INSTALLED_APPS, puis
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')
Dans ce cas, si vous ne souhaitez utiliser le champ que pour la représentation dans le site d'administration et de tels problèmes, vous pourriez mieux envisager de remplacer str () ou méthode unicode () de la classe telle qu'elle est mentionnée dans Django ici :
class Employee(models.Model):
# fields definitions
def __str__(self):
return self.lastname + ' ,' + self.firstname + ' ' + self.middlename