Say I have a serializer A
class SerializerA(ModelSerializer):
some_field = CharField()
some_other_field = CharField()
field_require_other_model = SerializerMethodField()
class Meta:
model = ModelA
fields = ('some_field', 'some_other_field', 'field_require_other_model')
def get_field_require_other_model(self, instance):
other_model_qs = ModelB.objects.filter(email=instance.email)
# say I want to get whatever that comes first
return other_model_qs.first().useful_info
As seen above, SerializerA uses ModelA for getting all the fields except that one in ModelB. I can get the info from ModelB doing what I did, but I don't know if this is the best way getting the data. I'm not sure if I need to hit database so many times or if there's a way to lazily evaluate it.
Also, what if I have another SerializerMethodField() that utilizes ModelB but for different info. Is this way still the best way to get the data?
How about using .annotate, annotating the other field onto modelA from modelB and then defining it as a charfield(or whatever the type is) on the serializer?
Something like
queryset = ModelA.objects.all().annotate(other_field_on_model_b=F('ModelB__other_field_on_model_b'))
then in the seralizer
class SerializerA(ModelSerializer):
some_field = CharField()
some_other_field = CharField()
other_field_on_model_b = CharField(required=False) #or whatever the field type is.
class Meta:
model = ModelA
fields = ('some_field', 'some_other_field', 'other_field_on_model_b')
Could do the annotation in get_queryset() or in the end point itself.
Having query like SELECT *, 'hello' AS world FROM myApp_myModel I'd like to serialize it to json.
Doesn't seem like a big deal, and there are plenty of similar questions on SO but none seems to give straight answer.
So far I've tried:
data = myModel.objects.raw(query)
# gives: ModelState is not serializable
json.dumps([dict(r.__dict__) for r in data])
# doesn't serialize 'world' column, only model fields:
serializers.serialize('json', data)
#dear God:
for r in data:
for k in dict(r.__dict__):
print(getattr(r,k))
The issue:
Builtin django core serializers are not ready to include extra fields ( from raw neither from annotation expression) It just takes model fields from _meta.local_fields.
You can see it at django django/core/serializers/base.py source code:
concrete_model = obj._meta.concrete_model #obj is an object model
...
for field in concrete_model._meta.local_fields:
if field.serialize or field is pk_parent:
if field.remote_field is None:
if (self.selected_fields is None
or field.attname in self.selected_fields):
self.handle_field(obj, field)
else:
if (self.selected_fields is None
or field.attname[:-3] in self.selected_fields):
self.handle_fk_field(obj, field)
django rest framework at rescue:
To solve your issue you can use a non builtin functionality. You can include a REST package in your project. For example django rest framework can handle extra fields:
from django.db.models import F
from aula.apps.alumnes.models import MyModel
from rest_framework.renderers import JSONRenderer
data=MyModel.objects.annotate(dummy = F('some_field') )
class MyModelSerializer(serializers.ModelSerializer):
dummy = serializers.CharField()
class Meta:
model = MyModel
fields = ('some_other_field','dummy')
read_only_fields = (
'dummy',
)
m=MyModelSerializer(data, many=True)
JSONRenderer().render(m.data)
You can create a DRF searializer for the task:
http://www.django-rest-framework.org/api-guide/serializers/
i.e.
class MyModelSerializer(serializers.ModelSerializer):
world = serializers.ReadOnlyField()
class Meta:
model = MyModel
fields = (world, ...)
you can also use serializer inheritance etc - see the docs.
There is a clean way you can do this using Django Rest Framework
First off did you know You can also execute queries containing fields that aren’t defined on the model when doing a Raw query
for example ( REF )
>>> people = Person.objects.raw('SELECT *, age(birth_date) AS age FROM myapp_person')
>>> for p in people:
... print("%s is %s." % (p.first_name, p.age))
John is 37.
Jane is 42.
That means you can use a standard serializer. You just need to tell the serializer what to do with fields that were not originally on the model consider the below. Needed to join 3 tables to a user. The user, the company they belong to and the companies membership. If your table has thousands of users and you did the standard serialiser method field, it would result in thousands of queries to get the related companies membership each time. so instead here was the solution I used
# api.py
class UserSAMAExportListApiView(ListAPIView):
serializer_class = UserExportSerializer
model = User
def get_queryset(self):
q = User.objects.raw(
"""
SELECT
[users_user].[id],
[users_user].[email],
[companies_company].[title] AS company__title,
[companies_company].[registration_number] AS company__registration_number,
[memberships_membership].number AS company__membership__number
FROM [users_user]
LEFT OUTER JOIN [dbo].[companies_company]
ON ([users_user].[company_id] = [companies_company].[id])
LEFT OUTER JOIN [memberships_membership]
ON ([companies_company].[id] = [memberships_membership].[company_id])
WHERE ([memberships_membership].[expiry_date] >= %s)
"""
, [date.today(),]
)
return q
Then just tell your standard serialiser that there are some new fields you defined
# serializers.py
class UserExportSerializer(ModelSerializer):
class Meta:
model = User
fields = (
'id',
'email',
'company__title',
'company__registration_number',
'company__membership__number',
)
def build_unknown_field(self, field_name, model_class):
"""
Return a two tuple of (cls, kwargs) to build a serializer field with. For fields that werent originally on
The model
"""
return fields.CharField, {'read_only': True}
And that's it DRF will handle the rest in a standard way and do proper serialization for you
Note you have to override the build_unknown_fields method. This is simply saying convert all the non-standard model fields to Text, if you want you can check the field name and convert to other formats here.
I have a django project with a model that looks like:
class Profile(models.Model):
#some other stuff
owner = models.OneToOneField(settings.AUTH_USER_MODEL)
last_modified = models.DateTimeField(default = timezone.now)
def __unicode__(self):
return self.owner.name
__unicode__.admin_order_field = 'owner__last_name'
My model admin looks something like:
class ProfileAdmin(admin.ModelAdmin):
ordering = ['-last_modified']
list_display = ['__unicode__', 'last_modified']
I would like for the admin to be sorted by last_modified by default (as it is now) but to be able to sort alphabetically by clicking on the top of the first column of the list display. I tried to add the __unicode__.admin_order_field line as described here, but that doesn't seem to have made any difference. Is what I want possible? If not why not?
You can only sort fields in the django admin interface if they are fields on your model or if they are fields you custom annotate in the get_queryset method of your ModelAdmin class--essentially fields created at the DB level. However, assuming you are deriving your __unicode__ or __str__ method from some fields on your model (and you are--from owner.name) you should be able to reference those fields and make the column sortable like so (though you could use this method to make the unicode field sortable on any model attribute you'd like):
class ProfileAdmin(admin.ModelAdmin):
def sortable_unicode(self, obj):
return obj.__unicode__()
sortable_unicode.short_description = 'Owner Name'
sortable_unicode.admin_order_field = 'owner__last_name'
ordering = ['-last_modified']
list_display = ['sortable_unicode', 'last_modified']
Though I do find it a bit strange that you will be displaying the owner's name but sorting on last_name. This might be a bit puzzling when you wonder why your sort order doesn't match the displayed name in the admin interface.
Can I get type of related field from a model queryset?
Let consider example model:
class Semester(models.Model):
active = models.BooleanField(default=False, verbose_name="Active")
class Subject(models.Model):
name = models.CharField(max_length=100, verbose_name="Name")
semester = models.ForeignKey(Semester, verbose_name="Semester")
if I have some field name in variable and queryset I can do this:
querySet = Subject.objects.all()
some_field_name = 'name'
field_type = querySet.model._meta.get_field(some_field_name).get_internal_type()
Is there any way to get related field type, for example:
querySet = Subject.objects.all()
some_field_name = 'semester__active'
field_type = ?
Try using get_field_by_name:
field_type = querySet.model._meta.get_field_by_name(some_field_name).get_internal_type()
From Django's source code:
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.
"""
Also try:
field = querySet.model._meta.get_field_by_name("semester")
field_type = field[0].rel.to._meta.get_field_by_name("active").get_internal_type()
Thanks for any help!
I find solution with some help from this answer:
main, related = some_field_name.split("__")
field_type = querySet.model._meta.get_field(main).rel.to._meta.get_field(related).get_internal_type()
Given a Django model, I'm trying to list all of its fields. I've seen some examples of doing this using the _meta model attribute, but doesn't the underscore in front of meta indicate that the _meta attribute is a private attribute and shouldn't be accessed directly? ... Because, for example, the layout of _meta could change in the future and not be a stable API?
Is _meta an exception to this rule? Is it stable and ready to use or is it considered bad practice to access it? Or is there a function or some other way to introspect the fields of a model without using the _meta attribute? Below is a list of some links showing how to do this using the _meta attribute
Any advice is much appreciated.
django object get/set field
http://www.djangofoo.com/80/get-list-model-fields
How to introspect django model fields?
_meta is private, but it's relatively stable. There are efforts to formalise it, document it and remove the underscore, which might happen before 1.3 or 1.4. I imagine effort will be made to ensure things are backwards compatible, because lots of people have been using it anyway.
If you're particularly concerned about compatibility, write a function that takes a model and returns the fields. This means if something does change in the future, you only have to change one function.
def get_model_fields(model):
return model._meta.fields
I believe this will return a list of Field objects. To get the value of each field from the instance, use getattr(instance, field.name).
Update: Django contributors are working on an API to replace the _Meta object as part of a Google Summer of Code. See:
- https://groups.google.com/forum/#!topic/django-developers/hD4roZq0wyk
- https://code.djangoproject.com/wiki/new_meta_api
I know this post is pretty old, but I just cared to tell anyone who is searching for the same thing that there is a public and official API to do this: get_fields() and get_field()
Usage:
fields = model._meta.get_fields()
my_field = model._meta.get_field('my_field')
https://docs.djangoproject.com/en/3.2/ref/models/meta/#retrieving-all-field-instances-of-a-model
get_fields() returns a tuple and each element is a Model field type, which can't be used directly as a string. So, field.name will return the field name
my_model_fields = [field.name for field in MyModel._meta.get_fields()]
The above code will return a list conatining all fields name
Example
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']
Now there is special method - get_fields()
>>> from django.contrib.auth.models import User
>>> User._meta.get_fields()
It accepts two parameters that can be used to control which fields are returned:
include_parents
True by default. Recursively includes fields defined on parent classes. If set to False, get_fields() will only search for fields declared directly on the current model. Fields from models that directly inherit from abstract models or proxy classes are considered to be local, not on the parent.
include_hidden
False by default. If set to True, get_fields() will include fields that are used to back other field’s functionality. This will also include any fields that have a related_name (such as ManyToManyField, or ForeignKey) that start with a “+”
This is something that is done by Django itself when building a form from a model. It is using the _meta attribute, but as Bernhard noted, it uses both _meta.fields and _meta.many_to_many. Looking at django.forms.models.fields_for_model, this is how you could do it:
opts = model._meta
for f in sorted(opts.fields + opts.many_to_many):
print '%s: %s' % (f.name, f)
fields = [f"{f.name}_id" if f.is_relation else f.name for f in model._meta.fields]
The model fields contained by _meta are listed in multiple locations as lists of the respective field objects. It may be easier to work with them as a dictionary where the keys are the field names.
In my opinion, this is most irredundant and expressive way to collect and organize the model field objects:
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
(See This example usage in django.forms.models.fields_for_model.)
How about this one.
fields = Model._meta.fields
If you need this for your admin site, there is also the ModelAdmin.get_fields method (docs), which returns a list of field name strings.
For example:
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
...
As per the django documentation 2.2 you can use:
To get all fields: Model._meta.get_fields()
To get an individual field: Model._meta.get_field('field name')
ex. Session._meta.get_field('expire_date')
Another way is add functions to the model and when you want to override the date you can call the function.
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()
instance._meta.get_fields() returns a list of all the fields (i.e. columns) in a Django model.
This method is used to introspect the model's fields, their types, and their relationships with other models. The method returns a list of Field objects, which represent the individual fields in the model.
For example, suppose you have a Django model called MyModel. You can use instance._meta.get_fields() to get a list of all the fields in the model:
from myapp.models import MyModel
my_instance = MyModel.objects.get(id=1)
fields = my_instance._meta.get_fields()
The fields variable will now contain a list of all the fields in the MyModel model, including fields such as id, name, created_at, and any related fields (such as foreign keys). You can use this list to access and manipulate the individual fields in the model.