Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
199 views
in Technique[技术] by (71.8m points)

python - Any way to fetch the through fields for an object linked via Many2Many field without knowing the column name in advance?

I am trying to write a generic method that can take any Django Model and returns it in a dictionary form.

So for example, if my models are defined thus (very generic):

class A(models.Model):
    somefieldA = models.TextField()
    m2mfield = models.ManyToManyField(B, through='AandB')

    def __unicode__(self):
        return self.somefieldA


class B(models.Model):
    somefieldB = models.TextField()

    def __unicode__(self):
        return self.somefieldB


class AandB(models.Model):
    a = models.ForeignKey(A)
    b = models.ForeignKey(B)
    field1 = models.DecimalField()
    field2 = models.TextField()
    field3 = models.DateField()

Now, assume we have an instance of the object A a_obj.

I can get all the related B objects using:

# This loop is there because I am working with other fields as well.
def instance_to_dict(instance):
    for field in instance._meta.get_fields():
        if field.many_to_many:
             m2m_mgr = getattr(instance, field.name)
             for idx, assoc_obj in enumerate(m2m_mgr.all()):
                 assoc_obj_str = str(assoc_obj)
                 # How to obtain the related through field values?
                 # m2m_mgr.through.objects.get() would need prior knowlegde
                 # of field name so get(a=instance, b=assoc_obj) is not possible
                 # m2m_mgr.through.objects.all() fetches all the objects
                 # in the Many to Many manager.

And then call instance_to_dict(a_obj). This method could be called by passing other models' instances. Ideally, I would like to create a dict of the obj and related "through" fields for any object. Is this possible to do?

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

In addition to the explicitly defined ManyToMany manager, there is also an implicit reverse relationship for the ForeignKey from AandB to A. So you can do something like this:

for field in instance._meta.get_fields(include_hidden=True):
    if field.one_to_many:  # reverse ForeignKey
        m2m_through_mgr = getattr(instance, field.get_accessor_name())  # e.g. aandb_set
        m2m_through_mgr.all()  # all related instances from the through table

Another approach is to go through the through table fields looking at field.related_model to see which one points back to your original table.

This all gets quite messy, but there should be enough meta information to do what you want. One obstacle is that the API isn't fully documented. Specifically, relation fields are represented by instances of the ManyToOneRel class, which as of Django 2.1 remains undocumented for reasons hinted at in the source code. Hence my use of the undocumented get_accessor_name() method.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...