Exclude related fields in model._meta.get_fields()
Asked Answered
C

4

5

I have a model currently defined like this:

class Category(models.Model):
    ID = models.AutoField()
    name = models.CharField()
    desc = models.CharField()

Another model Subcategory has a ForeignKey defined on Category.

When I run:

Category._meta.get_fields()

I get:

(<ManyToOneRel: siteapp.subcategory>, <django.db.models.fields.AutoField: ID>, <django.db.models.fields.CharField: name>, <django.db.models.fields.CharField: desc>)

However, I don't want the ManyToOneRel fields; I just want the others.

Currently, I am doing something like this:

from django.db.models.fields.reverse_related import ManyToOneRel
field_list = []
for field in modelClass._meta.get_fields():
    if not isinstance(field, ManyToOneRel):
        field_list.append(field)

However, is there a better way to do this, with or without using the model _meta API?

Constellate answered 17/5, 2018 at 8:24 Comment(4)
Can you explain what you are trying to do here? Where are you using this code and why?Cultivable
@DanielRoseman I need to get a list of attributes like max_length, etc. for each model field.Constellate
Yes but why? If you're creating a form, why not use a ModelForm which does all this for you?Cultivable
@DanielRoseman It's not for a form, it's for an internal page.Constellate
F
6

You could use the concrete_fields property.

Category._meta.concrete_fields

However this is an internal Django API, and it may be better to use get_fields() with your own filtering, even though it may be a little more verbose.

Fyn answered 17/5, 2018 at 10:50 Comment(0)
C
0

I had the same issue creating a serializer mixin that treated only GenericRelaTion fields. Unfortunately, when you use the get_fields() the ManyToOneRel in somecases appear principally when you need to get attname of a field. Therefore I created a function that treates this issue skiping all the ManyToOneRel from fields:

def get_generic_relation_fields(self):
    """
    This function returns all the GenericRelation 
    fields needed to return the values that are 
    related such as polymorphic models.
    """
    other_field = [field for field in self.Meta.model._meta.get_fields()]
    fields = [field.attname for field in self.Meta.model._meta.get_fields() if not isinstance(field, (ManyToOneRel))]
    generic_relation_fields = []
    for field in fields:
        get_type = self.Meta.model._meta.get_field(field)
        field_type = get_type.__class__.__name__
    
        if field_type == "GenericRelation": #<----- change field name to filter different fields.
                generic_relation_fields.append(field)

    return generic_relation_fields

Usage:

class MyModel(models.Model):
    . . .
    first_name = GenericRelation(MyUserPolymorphic)
    last_name = GenericRelation(MyUserPolymorphic)
    whatever = GenericRelation(MyUserPolymorphic)


class MyModelSerializer(serializer.ModelSerializer)

    def create(self, validated_data):

        fields = [field for field in get_generic_relation_fields(self)]
        print("FIELDS ---->", fields)
        . . .

output on POST:

FIELDS ----> ['first_name', 'last_name', 'whatever']

However, there I added 'GenericRelation' field you can add other fields to be filtered and treated as you want.

Cuticula answered 6/12, 2021 at 18:27 Comment(0)
A
0

I have been looking for a solution for this, and I've ended up writing my own script which while not the most clean pythonic code, works. I thought to put it here if someone stumbles upon it in the future.

[field for field in fields if str(type(field)) !=  "<class 'django.db.models.fields.related.ForeignKey'>"]

I have also used this script as follows to get a dict in the format of {field_name:field_value}

{
   field.name: (
     getattr(self, field.name)
     if str(type(field))
     != "<class 'django.db.models.fields.related.ForeignKey'>"
     else getattr(self, field.name).id
     )
   for field in fields
   if str(type(field)) 
    != "<class 'django.db.models.fields.reverse_related.ManyToOneRel'>"}

I had to do an additional check for ForeignKey constraints as usually showing the field value didn't make sense in that case

Antalya answered 13/1, 2022 at 13:13 Comment(0)
A
0

If someone looking for this in 2024:

You can use related_objects:

opts = model_instance._meta
fields = opts.get_fields()
related = opts.related_objects

for field in fields:
    if field not in related:
        print(type(field))
        print(field)

Or filter relatitionship classes:

opts = model_instance._meta
fields = [field for field in opts.get_fields() if not isinstance(field, 
(ManyToOneRel, ManyToManyRel, OneToOneRel))]
Accusatory answered 5/6 at 13:20 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.