Ottieni i campi del modello in Django


121

Dato un modello Django, sto cercando di elencare tutti i suoi campi. Ho visto alcuni esempi di questa operazione utilizzando l'attributo del modello _meta, ma il carattere di sottolineatura davanti a meta non indica che l'attributo _meta è un attributo privato e non dovrebbe essere accessibile direttamente? ... Perché, ad esempio, il layout di _meta potrebbe cambiare in futuro e non essere un'API stabile?

_Meta è un'eccezione a questa regola? È stabile e pronto per l'uso o è considerata una cattiva pratica accedervi? O esiste una funzione o un altro modo per analizzare i campi di un modello senza utilizzare l'attributo _meta? Di seguito è riportato un elenco di alcuni collegamenti che mostrano come eseguire questa operazione utilizzando l'attributo _meta

Ogni consiglio è molto apprezzato.

campo get / set oggetto django

http://www.djangofoo.com/80/get-list-model-fields

Come analizzare i campi del modello Django?


Risposte:


144

_metaè privato, ma è relativamente stabile. Ci sono sforzi per formalizzarlo, documentarlo e rimuovere il carattere di sottolineatura, cosa che potrebbe accadere prima della 1.3 o 1.4. Immagino che verrà fatto uno sforzo per garantire che le cose siano retrocompatibili, perché molte persone lo usano comunque.

Se sei particolarmente preoccupato per la compatibilità, scrivi una funzione che prenda un modello e restituisca i campi. Ciò significa che se qualcosa cambia in futuro, devi solo cambiare una funzione.

def get_model_fields(model):
    return model._meta.fields

Credo che questo restituirà un elenco di Fieldoggetti. Per ottenere il valore di ogni campo dall'istanza, utilizzare getattr(instance, field.name).

Aggiornamento: i collaboratori di Django stanno lavorando a un'API per sostituire l'oggetto _Meta come parte di un Google Summer of Code. Vedi:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api


45
Dovresti anche essere consapevole del fatto che se hai bisogno anche dei campi molti-a-molti devi accedere model._meta.many_to_many!
Bernhard Vallant

1
Grazie Will. Buono a sapersi che anche altre persone usano _meta. Mi piace l'idea di avere una funzione wrapper. Lazerscience, grazie anche a te. Buono a sapersi che esiste un bel metodo per ottenere i campi many_to_many. Joe
Joe J

3
django/core/management/commands/loaddata.pyutilizza _meta per esplorare l'albero di app, modelli e campi. Bel modello da seguire ... e puoi scommettere che è il "modo ufficiale".
piani cottura

2
Se stai usando chiavi esterne generiche dovresti anche controllare_meta.virtual_fields
andrei1089


97

So che questo post è piuttosto vecchio, ma volevo solo dire a chiunque stia cercando la stessa cosa che esiste un'API pubblica e ufficiale per farlo: get_fields()eget_field()

Uso:

fields = model._meta.get_fields()
my_field = model._meta.get_field('my_field')

https://docs.djangoproject.com/en/1.8/ref/models/meta/


6
Questa è in realtà la risposta giusta per la versione più recente di django.
chhantyal


3
Una domanda: se queste sono "pubbliche e ufficiali", perché sono ancora sotto _meta?
krubo

9

Ora c'è un metodo speciale: get_fields ()

    >>> from django.contrib.auth.models import User
    >>> User._meta.get_fields()

Accetta due parametri che possono essere utilizzati per controllare quali campi vengono restituiti:

  • include_parents

    Vero per impostazione predefinita. Include ricorsivamente i campi definiti nelle classi padre. Se impostato su False, get_fields () cercherà solo i campi dichiarati direttamente nel modello corrente. I campi dei modelli che ereditano direttamente da modelli astratti o classi proxy sono considerati locali, non del genitore.

  • include_hidden

    Falso per impostazione predefinita. Se impostato su True, get_fields () includerà i campi utilizzati per supportare le funzionalità di altri campi. Ciò includerà anche tutti i campi con un nome_relativo (come ManyToManyField o ForeignKey) che iniziano con un "+"


9

get_fields()restituisce a tuplee ogni elemento è un Model fieldtipo, che non può essere utilizzato direttamente come stringa. Quindi, field.namerestituirà il nome del campo

my_model_fields = [field.name for field in MyModel._meta.get_fields()]
Il codice sopra restituirà un elenco contenente tutti i campi nome

Esempio

In [11]: from django.contrib.auth.models import User

In [12]: User._meta.get_fields()
Out[12]: 
(<ManyToOneRel: admin.logentry>,
 <django.db.models.fields.AutoField: id>,
 <django.db.models.fields.CharField: password>,
 <django.db.models.fields.DateTimeField: last_login>,
 <django.db.models.fields.BooleanField: is_superuser>,
 <django.db.models.fields.CharField: username>,
 <django.db.models.fields.CharField: first_name>,
 <django.db.models.fields.CharField: last_name>,
 <django.db.models.fields.EmailField: email>,
 <django.db.models.fields.BooleanField: is_staff>,
 <django.db.models.fields.BooleanField: is_active>,
 <django.db.models.fields.DateTimeField: date_joined>,
 <django.db.models.fields.related.ManyToManyField: groups>,
 <django.db.models.fields.related.ManyToManyField: user_permissions>)

In [13]: [field.name for field in User._meta.get_fields()]
Out[13]: 
['logentry',
 'id',
 'password',
 'last_login',
 'is_superuser',
 'username',
 'first_name',
 'last_name',
 'email',
 'is_staff',
 'is_active',
 'date_joined',
 'groups',
 'user_permissions']

Bella risposta! Grazie!
Tms91

8

Questo è qualcosa che viene fatto da Django stesso quando costruisce un form da un modello. Utilizza l'attributo _meta, ma come ha notato Bernhard, utilizza sia _meta.fields che _meta.many_to_many. Guardando django.forms.models.fields_for_model, ecco come potresti farlo:

opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
    print '%s: %s' % (f.name, f)

4

I campi del modello contenuti in _meta sono elencati in più posizioni come elenchi dei rispettivi oggetti campo. Potrebbe essere più facile lavorare con loro come un dizionario in cui le chiavi sono i nomi dei campi.

A mio parere, questo è il modo più irridondante ed espressivo per raccogliere e organizzare gli oggetti del campo del modello:

def get_model_fields(model):
  fields = {}
  options = model._meta
  for field in sorted(options.concrete_fields + options.many_to_many + options.virtual_fields):
    fields[field.name] = field
  return fields

(Vedi questo esempio di utilizzo in django.forms.models.fields_for_model.)


2
Potresti voler accompagnare il tuo codice con una breve descrizione di ciò che sta facendo.
Bas van Dijk

2

Che ne dici di questo.

fields = Model._meta.fields

1
Immagino che si possa voler accedere alle proprietà di un campo. Inoltre, come sottolineato nelle risposte precedenti, ci sono many_to_manye virtual_fields.
Jocke

@Jocke hai ragione. Potrebbe sorgere la necessità di accedere a un altro attributo. Risposta modificata.
JDE876

2

Secondo la documentazione 2.2 di django puoi usare:

Per ottenere tutti i campi: Model._meta.get_fields()

Per ottenere un singolo campo: Model._meta.get_field('field name')

ex. Session._meta.get_field('expire_date')


1

Se hai bisogno di questo per il tuo sito di amministrazione , c'è anche il ModelAdmin.get_fieldsmetodo ( docs ), che restituisce un listnome di campo strings.

Per esempio:

class MyModelAdmin(admin.ModelAdmin):
    # extending change_view, just as an example
    def change_view(self, request, object_id=None, form_url='', extra_context=None):
        # get the model field names
        field_names = self.get_fields(request)
        # use the field names
        ...

0

Un altro modo è aggiungere funzioni al modello e quando vuoi sovrascrivere la data puoi chiamare la funzione.

class MyModel(models.Model):
    name = models.CharField(max_length=256)
    created = models.DateTimeField(auto_now_add=True)
    modified = models.DateTimeField(auto_now=True)

    def set_created_date(self, created_date):
        field = self._meta.get_field('created')
        field.auto_now_add = False
        self.created = created_date

    def set_modified_date(self, modified_date):
        field = self._meta.get_field('modified')
        field.auto_now = False
        self.modified = modified_date

my_model = MyModel(name='test')
my_model.set_modified_date(new_date)
my_model.set_created_date(new_date)
my_model.save()
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.