How to introspect django model fields? - python

I am trying to obtain class information on a field inside a model, when I only know name of the field and name of the model (both plain strings). How is it possible?
I can load the model dynamically:
from django.db import models
model = models.get_model('myapp','mymodel')
Now I have field - 'myfield' - how can I get the class of that field?
If the field is relational - how to get related field?
Thanks a bunch!

You can use model's _meta attribute to get field object and from field you can get relationship and much more e.g. consider a employee table which has a foreign key to a department table
In [1]: from django.db import models
In [2]: model = models.get_model('timeapp', 'Employee')
In [3]: dep_field = model._meta.get_field_by_name('department')
In [4]: dep_field[0].target_field
Out[4]: 'id'
In [5]: dep_field[0].related_model
Out[5]: <class 'timesite.timeapp.models.Department'>
from django/db/models/options.py
def get_field_by_name(self, name):
"""
Returns the (field_object, model, direct, m2m), where field_object is
the Field instance for the given name, model is the model containing
this field (None for local fields), direct is True if the field exists
on this model, and m2m is True for many-to-many relations. When
'direct' is False, 'field_object' is the corresponding RelatedObject
for this field (since the field doesn't have an instance associated
with it).
Uses a cache internally, so after the first access, this is very fast.
"""

The answer from Anurag Uniyal to use get_field_by_name is now (5 years later) outdated as get_field_by_name is deprecated.
Django will give you the following hint:
RemovedInDjango110Warning: 'get_field_by_name is an unofficial API
that has been deprecated. You may be able to replace it with
'get_field()'
API docs for get_field are here.

If you would like to see ALL fields on a Django model object you can simply introspect it by calling ._meta.get_fields() on the class (or an instantiated model object) to get at list of all fields. This API is current with the latest version of Django.
Example:
from django.contrib.auth.models import User
User._meta.get_fields()
This will return a tuple of all model fields. Documentation can be found HERE.

django.db.models.loading.get_model() has been removed in django 1.9.
You are supposed to use django.apps instead.
'get_field_by_name is an unofficial API that has been deprecated in Django 1.10. You may be able to replace it with 'get_field()'
>>> from django.apps import apps
>>> from polls.models import Question
>>> QuestionModel = apps.get_model('polls','Question')
>>> QuestionModel._meta.get_field('pub_date')
>>> QuestionModel._meta.get_fields()
(<ManyToOneRel: polls.choice>, <django.db.models.fields.AutoField: id>, <django.db.models.fields.CharField: question_text>, <django.db.models.fields.DateTimeField: pub_date>)
link to issue

Related

how to use dictionary field in Django?

I need to use dictionary field on a Django Model.
for example, on a data
name = Kim, user_id = 12902938291, dictionary = {'yo' : 'drop', 'the': 'beat'}
I know about model serialization but it doesn't satisfy my requirement.
How to use dictionary field in Django?
Another clean and fast solution can be found here: https://docs.djangoproject.com/en/3.1/ref/models/fields/#django.db.models.JSONField
For convenience I copied the simple instructions.
Usage
from django.db import models
class MyModel(models.Model):
json = JSONField()

Reverse relation (one-to-one) lookup not working in Django view (django-plans)

I've just installed django-plans. They link a UserPlan object to the used settings.AUTH_USER_MODEL through a one-to-one relation. As I understand it, this relation does not require a related_name for reverse lookups. Therefore, it should be callable using the Class name in without Caps, e.g. userplan.
My problem is that the reverse lookup works in the python shell:
>>> from profiles.models import CustomUser
>>> s=CustomUser.objects.get(email="test#test.com")
>>> s <CustomUser: test#test.com>
>>> s.userplan <UserPlan: test#test.com [Starter]>
But when I use the code in a view, it returns an Attribute Error:
'CustomUser' object has no attribute 'userplan'
This is the code I'm using in the view:
u = CustomUser.objects.get(email="test#test.com")
up = u.userplan
First I thought it had to do with request.user being a SimpleLazyObject, but even when fetching a "real" user it didn't seem to work in the view.
Any suggestions?
P.S. As you might have noticed, we're using a custom user model.

Update only some field with create_update.update_object

I've defined a models.py with a "FirstClass" which contains a ForeignKey relathionship to "SecondClass". The relathionship can't be Null.
The SecondClass is very expansive (90.000 records), and when i display the FirstClass html form, it requires too many time generating the "select box" field.
Therefore, when I let user update the object (I use create_update.update_object generic view), i don't want to display and update the value of the foreignkey field, but i don't know how to do this...
Create a ModelForm and pass it into the view, according to the docs.
Since the foreign key should always exist upon creation, it's safe to ignore it in the update.
class MyModelForm(forms.ModelForm):
class Meta:
model = FirstClass
exclude = ('SecondClass',)
# urls.py
(r'^foo/(?P<object_id>\d+)/$','django.views.generic.create_update.update_object',
{'form_class': MyModelForm})

Enable Django format localization by default

This is about the Format Localization feature that was implemented in Django 1.2.
In order to use this feature, you must add a localize=True parameter to all your form fields. I am trying to implement this localization in my app but the problem is that I am creating my forms dynamically by using the inlineformset_factory method that Django provides, so I cannot simply add a new parameter to the form field.
So I tried to enable this feature by default in all models, without needing to add a new parameter for all fields. I created a BaseInlineFormSet subclass and hard-coded the parameter in it.
class MyBaseInlineFormSet(BaseInlineFormSet):
def __init__(self, *args, **kwargs):
super(MyBaseInlineFormSet, self).__init__(*args, **kwargs)
for form in self.forms:
for key, field in form.fields.iteritems():
if field.__class__ == forms.DecimalField:
form.fields[key].localize = True
That worked only 50%. When submitted, the forms are being validated correctly by Django now (it's accepting commas instead of only dot) but the fields are still being displayed incorrectly.
I guess I could javascript my way out of this problem, but I prefer to avoid doing that.
Any ideas on how to solve this?
Django 1.2 is 3 years old now. Django 1.6 provides a nice way to solve your dilemma:
From the docs:
By default, the fields in a ModelForm will not localize their data. To enable localization for fields, you can use the localized_fields attribute on the Meta class.
>>> from django.forms import ModelForm
>>> from myapp.models import Author
>>> class AuthorForm(ModelForm):
... class Meta:
... model = Author
... localized_fields = ('birth_date',)
If localized_fields is set to the special value __all__, all fields will be localized
I've have not used it - (still to picka project to develop in Django) -
but it seens to be the case of subclassing -
Instead of having your fields inheriting from forms.DecimalField, make them be a:
class LocalizedDecimalField(forms.DecimalField):
localize = True

How to find out whether a model's column is a foreign key?

I'm dynamically storing information in the database depending on the request:
// table, id and column are provided by the request
table_obj = getattr(models, table)
record = table_obj.objects.get(pk=id)
setattr(record, column, request.POST['value'])
The problem is that request.POST['value'] sometimes contains a foreign record's primary key (i.e. an integer) whereas Django expects the column's value to be an object of type ForeignModel:
Cannot assign "u'122'": "ModelA.b" must be a "ModelB" instance.
Now, is there an elegant way to dynamically check whether b is a column containing foreign keys and what model these keys are linked to? (So that I can load the foreign record by it's primary key and assign it to ModelA?) Or doesn't Django provide information like this to the programmer so I really have to get my hands dirty and use isinstance() on the foreign-key column?
You can use get_field_by_name on the models _meta object:
from django.db.models import ForeignKey
def get_fk_model(model, fieldname):
"""Returns None if not foreignkey, otherswise the relevant model"""
field_object, model, direct, m2m = model._meta.get_field_by_name(fieldname)
if not m2m and direct and isinstance(field_object, ForeignKey):
return field_object.rel.to
return None
Assuming you had a model class MyModel you would use this thus:
fk_model = get_fk_model(MyModel, 'fieldname')
Simple one liner to find all the relations to other models that exist in a model:
In [8]: relations = [f for f in Model._meta.get_fields() if (f.many_to_one or f.one_to_one) and f.auto_created]
Above will give a list of all the models with their relations.
Example:
In [9]: relations
Out[9]:
[<ManyToOneRel: app1.model1>,
<ManyToOneRel: app2.model1>,
<OneToOneRel: app1.model2>,
<OneToOneRel: app3.model5>,
<OneToOneRel: app5.model1>]
I encountered the same use case, and the accepted answer did not work for me directly. I am using Django 1.2 if it's relevant. Instead, I used the get_field_by_name method as follows.
def get_foreign_keys(self):
foreign_keys = []
for field in self._meta.fields:
if isinstance(self._meta.get_field_by_name(field.name)[0], models.ForeignKey):
foreign_keys.append(field.name)
if not foreign_keys:
return None
return foreign_keys
This is a method define inside a class. For my case, what I needed are the names of the ForeignKey fields. Cheers!
Explore the "ModelChoiceField" fields. Can they solve your problem putting foreign keys into forms for you; rather than doing that yourself.
http://docs.djangoproject.com/en/1.1/ref/forms/fields/#fields-which-handle-relationships
record = forms.ModelChoiceField(queryset=table_obj.objects.all())

Categories

Resources