Django: Obter modelo da string?

133

No Django, você pode especificar relacionamentos como:

author = ForeignKey('Person')

E, em seguida, internamente, ele deve converter a string "Person" no modelo Person.

Onde está a função que faz isso? Eu quero usá-lo, mas não consigo encontrá-lo.

mpen
fonte

Respostas:

167

No Django 3.0, é AppConfig.get_model(model_name, require_ready=True)

A partir do Django 1.9, o método é django.apps.AppConfig.get_model(model_name).
- danihp

A partir do Django 1.7, ele django.db.models.loadingfoi descontinuado (a ser removido no 1.9) em favor do novo sistema de carregamento de aplicativos.
- Scott Woodall


Encontrei. Está definido aqui:

from django.db.models.loading import get_model

Definido como:

def get_model(self, app_label, model_name, seed_cache=True):
mpen
fonte
11
Esta solução foi descontinuada no Django 1.7, consulte esta outra resposta para a solução: stackoverflow.com/a/26126935/155987
Tim Saylor
4
Olá @mpen, sugiro que atualize sua resposta. No Django 1.9 get_model é definido em AppConfig :from django.apps import AppConfig
herrera dani
1
O django.apps.AppConfig é um tipo de gerador que não pode funcionar como objeto de modelo.
Neeraj Kumar
2
No Django 1.7+, é melhor usar get_model () no registro do aplicativo Django, que está disponível via django.apps.apps.get_model(model_name). Os objetos AppConfig destinam-se a uma finalidade diferente e exigem que você crie uma instância do AppConfig para poder chamar get_model().
zlovelady
2
O Django 1.11 precisa da resposta fornecida por @luke_aus
Georg Zimmer
132

django.db.models.loadingfoi descontinuado no Django 1.7 ( removido em 1.9 ) em favor do novo sistema de carregamento de aplicativos .

Django 1.7 docs nos dar o seguinte em vez disso:

>>> from django.apps import apps
>>> User = apps.get_model(app_label='auth', model_name='User')
>>> print(User)
<class 'django.contrib.auth.models.User'>
Scott Woodall
fonte
Eu costumava usar a função "localizar" do pydoc ... não tenho certeza da diferença aqui, mas para permanecer no Django, mudei para esse método. Obrigado.
warath-coder
61

apenas para quem fica preso (como eu):

from django.apps import apps

model = apps.get_model('app_name', 'model_name')

app_name devem ser listados usando aspas, assim como model_name (ou seja, não tente importá-lo)

get_model aceita letras minúsculas ou maiúsculas 'model_name'

lukeaus
fonte
32

A maioria das "strings" do modelo aparece como "appname.modelname", portanto, você pode usar essa variação em get_model

from django.db.models.loading import get_model

your_model = get_model ( *your_string.split('.',1) )

A parte do código Django que geralmente transforma essas cordas em um modelo é um pouco mais complexa Este partir de django/db/models/fields/related.py:

    try:
        app_label, model_name = relation.split(".")
    except ValueError:
        # If we can't split, assume a model in current app
        app_label = cls._meta.app_label
        model_name = relation
    except AttributeError:
        # If it doesn't have a split it's actually a model class
        app_label = relation._meta.app_label
        model_name = relation._meta.object_name

# Try to look up the related model, and if it's already loaded resolve the
# string right away. If get_model returns None, it means that the related
# model isn't loaded yet, so we need to pend the relation until the class
# is prepared.
model = get_model(app_label, model_name,
                  seed_cache=False, only_installed=False)

Para mim, esse parece ser um bom argumento para dividir isso em uma única função no código principal. No entanto, se você souber que suas seqüências de caracteres estão no formato "App.Model", os dois forros acima funcionarão.

Ch'marr
fonte
3
Eu acho que a segunda linha deve ser: your_model = get_model(*your_string.rsplit('.', 1)). Às vezes, o rótulo do aplicativo é um formato pontilhado; no entanto, o nome do modelo é sempre um identificador válido.
Rockallite
1
Parece que isso não é necessário no novo apps.get_model. "Como atalho, esse método também aceita um único argumento no formulário app_label.model_name."
precisa
16

A maneira abençoada de fazer isso no Django 1.7+ é:

import django
model_cls = django.apps.apps.get_model('app_name', 'model_name')

Portanto, no exemplo canônico de todos os tutoriais de estrutura:

import django
entry_cls = django.apps.apps.get_model('blog', 'entry')  # Case insensitive
Craig Labenz
fonte
9

Caso não saiba em qual aplicativo seu modelo existe, você pode pesquisá-lo desta maneira:

from django.contrib.contenttypes.models import ContentType 
ct = ContentType.objects.get(model='your_model_name') 
model = ct.model_class()

Lembre-se de que o seu_model_name deve estar em minúsculas.

Ola Nguyen Van
fonte
4

Não sei ao certo onde é feito no Django, mas você pode fazer isso.

Mapeando o nome da classe para a string via reflexão.

classes = [Person,Child,Parent]
def find_class(name):
 for clls in classes:
  if clls.__class__.__name__ == name:
   return clls
jbcurtin
fonte
1
É clls .__ class __.__ name__ ou apenas clls .__ name__?
Vinayak Kaniyarakkal
4

Outra versão com menos código para os preguiçosos. Testado no Django 2+

from django.apps import apps
model = apps.get_model("appname.ModelName") # e.g "accounts.User"
Glicerina
fonte
1

Aqui está uma abordagem menos específica do django para obter uma classe da string:

mymodels = ['ModelA', 'ModelB']
model_list = __import__('<appname>.models', fromlist=mymodels)
model_a = getattr(model_list, 'ModelA')

ou você pode usar importlib como mostrado aqui :

import importlib
myapp_models = importlib.import_module('<appname>.models')
model_a = getattr(myapp_models, 'ModelA')
Schubisu
fonte